/*
 *  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.  */

#define GRUB	1
#include <etherboot.h>

void
sleep (int secs)
{
  unsigned long tmo = currticks () + secs;

  while (currticks () < tmo)
    ;
}

void
twiddle (void)
{
  static unsigned long lastticks = 0;
  static int count = 0;
  static const char tiddles[]="-\\|/";
  unsigned long ticks;

  if (debug)
    {
      if ((ticks = currticks ()) == lastticks)
	return;
      
      lastticks = ticks;
      grub_putchar (tiddles[(count++) & 3]);
      grub_putchar ('\b');
    }
}

/* 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;
}
