root/usr/src/grub/grub-0.97/netboot/misc.c
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2000,2001,2002  Free Software Foundation, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* Based on "src/misc.c" in etherboot-5.0.5.  */

#include "grub.h"
#include "timer.h"

#include "nic.h"

/**************************************************************************
RANDOM - compute a random number between 0 and 2147483647L or 2147483562?
**************************************************************************/
int32_t random(void)
{
        static int32_t seed = 0;
        int32_t q;
        if (!seed) /* Initialize linear congruential generator */
                seed = currticks() + *(int32_t *)&arptable[ARP_CLIENT].node
                       + ((int16_t *)arptable[ARP_CLIENT].node)[2];
        /* simplified version of the LCG given in Bruce Schneier's
           "Applied Cryptography" */
        q = seed/53668;
        if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563L;
        return seed;
}

/**************************************************************************
POLL INTERRUPTIONS
**************************************************************************/
void poll_interruptions(void)
{
        if (checkkey() != -1 && ASCII_CHAR(getkey()) == K_INTR) {
                user_abort++;
        }
}

/**************************************************************************
SLEEP
**************************************************************************/
void sleep(int secs)
{
        unsigned long tmo;

        for (tmo = currticks()+secs*TICKS_PER_SEC; currticks() < tmo; ) {
                poll_interruptions();
        }
}

/**************************************************************************
INTERRUPTIBLE SLEEP
**************************************************************************/
void interruptible_sleep(int secs)
{
        printf("<sleep>\n");
        return sleep(secs);
}

/**************************************************************************
TWIDDLE
**************************************************************************/
void twiddle(void)
{
#ifdef BAR_PROGRESS
        static int count=0;
        static const char tiddles[]="-\\|/";
        static unsigned long lastticks = 0;
        unsigned long ticks;
#endif
#ifdef FREEBSD_PXEEMU
        extern char pxeemu_nbp_active;
        if(pxeemu_nbp_active != 0)
                return;
#endif
#ifdef  BAR_PROGRESS
        /* Limit the maximum rate at which characters are printed */
        ticks = currticks();
        if ((lastticks + (TICKS_PER_SEC/18)) > ticks)
                return;
        lastticks = ticks;

        putchar(tiddles[(count++)&3]);
        putchar('\b');
#else
        //putchar('.');
#endif  /* BAR_PROGRESS */
}


/* Because Etherboot uses its own formats for the printf family,
   define separate definitions from GRUB.  */
/**************************************************************************
PRINTF and friends

        Formats:
                %[#]x   - 4 bytes long (8 hex digits, lower case)
                %[#]X   - 4 bytes long (8 hex digits, upper case)
                %[#]hx  - 2 bytes int (4 hex digits, lower case)
                %[#]hX  - 2 bytes int (4 hex digits, upper case)
                %[#]hhx - 1 byte int (2 hex digits, lower case)
                %[#]hhX - 1 byte int (2 hex digits, upper case)
                        - optional # prefixes 0x or 0X
                %d      - decimal int
                %c      - char
                %s      - string
                %@      - Internet address in ddd.ddd.ddd.ddd notation
                %!      - Ethernet address in xx:xx:xx:xx:xx:xx notation
        Note: width specification not supported
**************************************************************************/
static int
etherboot_vsprintf (char *buf, const char *fmt, const int *dp)
{
  char *p, *s;
  
  s = buf;
  for ( ; *fmt != '\0'; ++fmt)
    {
      if (*fmt != '%')
        {
          buf ? *s++ = *fmt : grub_putchar (*fmt);
          continue;
        }
      
      if (*++fmt == 's')
        {
          for (p = (char *) *dp++; *p != '\0'; p++)
            buf ? *s++ = *p : grub_putchar (*p);
        }
      else
        {
          /* Length of item is bounded */
          char tmp[20], *q = tmp;
          int alt = 0;
          int shift = 28;
          
          if (*fmt == '#')
            {
              alt = 1;
              fmt++;
            }
          
          if (*fmt == 'h')
            {
              shift = 12;
              fmt++;
            }
          
          if (*fmt == 'h')
            {
              shift = 4;
              fmt++;
            }
          
          /*
           * Before each format q points to tmp buffer
           * After each format q points past end of item
           */
          if ((*fmt | 0x20) == 'x')
            {
              /* With x86 gcc, sizeof(long) == sizeof(int) */
              const long *lp = (const long *) dp;
              long h = *lp++;
              int ncase = (*fmt & 0x20);
              
              dp = (const int *) lp;
              if (alt)
                {
                  *q++ = '0';
                  *q++ = 'X' | ncase;
                }
              for (; shift >= 0; shift -= 4)
                *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
            }
          else if (*fmt == 'd')
            {
              int i = *dp++;
              char *r;
              
              if (i < 0)
                {
                  *q++ = '-';
                  i = -i;
                }
              
              p = q;            /* save beginning of digits */
              do
                {
                  *q++ = '0' + (i % 10);
                  i /= 10;
                }
              while (i);
              
              /* reverse digits, stop in middle */
              r = q;            /* don't alter q */
              while (--r > p)
                {
                  i = *r;
                  *r = *p;
                  *p++ = i;
                }
            }
          else if (*fmt == '@')
            {
              unsigned char *r;
              union
              {
                long            l;
                unsigned char   c[4];
              }
              u;
              const long *lp = (const long *) dp;
              
              u.l = *lp++;
              dp = (const int *) lp;
              
              for (r = &u.c[0]; r < &u.c[4]; ++r)
                q += etherboot_sprintf (q, "%d.", *r);
              
              --q;
            }
          else if (*fmt == '!')
            {
              char *r;
              p = (char *) *dp++;
              
              for (r = p + ETH_ALEN; p < r; ++p)
                q += etherboot_sprintf (q, "%hhX:", *p);
              
              --q;
            }
          else if (*fmt == 'c')
            *q++ = *dp++;
          else
            *q++ = *fmt;
          
          /* now output the saved string */
          for (p = tmp; p < q; ++p)
            buf ? *s++ = *p : grub_putchar (*p);
        }
    }
  
  if (buf)
    *s = '\0';
  
  return (s - buf);
}

int
etherboot_sprintf (char *buf, const char *fmt, ...)
{
  return etherboot_vsprintf (buf, fmt, ((const int *) &fmt) + 1);
}

void
etherboot_printf (const char *fmt, ...)
{
  (void) etherboot_vsprintf (0, fmt, ((const int *) &fmt) + 1);
}

int
inet_aton (char *p, in_addr *addr)
{
  unsigned long ip = 0;
  int val;
  int i;
  
  for (i = 0; i < 4; i++)
    {
      val = getdec (&p);
      
      if (val < 0 || val > 255)
        return 0;
      
      if (i != 3 && *p++ != '.')
        return 0;
      
      ip = (ip << 8) | val;
    }

  addr->s_addr = htonl (ip);

  return 1;
}

int
getdec (char **ptr)
{
  char *p = *ptr;
  int ret = 0;
  
  if (*p < '0' || *p > '9')
    return -1;
  
  while (*p >= '0' && *p <= '9')
    {
      ret = ret * 10 + (*p - '0');
      p++;
    }
  
  *ptr = p;
  
  return ret;
}