/*
	format.C

	Format function used by v_cprintf () and sprintf ().

	Copyright (C) 1993, Geoff Friesen B.Sc.
	All rights reserved.
*/

#define INCL_FORMAT

void _prt10	(unsigned num, char *str);
void _prt16	(unsigned num, char *str, int hexcase);
void _prtl10	(unsigned long num, char *str);
void _prtl16	(unsigned long num, char *str, int hexcase);

int pascal format (void (*func) (int), const char *fmt, void *ap)
{
   int c;		/* current character in format string */
   int i;		/* loop index */
   int sign;		/* sign character for positive numeric output */
   char fill;		/* fill character (' ' or '0') */
   int width;		/* field width specifier */
   char *str;		/* running pointer */
   int digit1;		/* offset to add to first numeric digit */
   int length;		/* length of string "str" */
   int leading;		/* # of leading/trailing fill characters */
   int hexcase;		/* upper/lower-case hex digits flag */
   int leftjust;	/* 0 = right-justified else left-justified */
   int longflag;	/* not zero for long numerics */
   int count = 0;	/* current number of characters output */
   char *percent;	/* location of most recent percent sign */
   char string [20];	/* temporary buffer */

   /* ---------------------------------------- */
   /* Echo literals and formatted data to func */
   /* ---------------------------------------- */

   for (;;)
   {
	/* -------------------------------- */
	/* Initialize variables to defaults */
	/* -------------------------------- */

	sign = ' ';  	/* positive characters begin with ' ' */
	fill = ' ';	/* fill character is ' ' */
	width = 0;	/* width of field */
	hexcase = 'L';	/* assume lower-case hex digits */
	leftjust = 0;	/* numbers are right-justified */
	longflag = 0;	/* numbers are interpreted as ints */

	/* -------------------------------------------- */
	/* Echo literals until '%' or end of fmt string */
	/* -------------------------------------------- */

	while ((c = *fmt++) != '%')
	{
	   if (!c)
	       return count;	/* return if end of format string */

	   (*func) (c);
	   count++;
	}

	/* ---------------------- */
	/* Obtain location of '%' */
	/* ---------------------- */

	percent = (char *) fmt-1;

	/* ---------------- */
	/* Echo "%%" as '%' */
	/* ---------------- */

	if (*fmt == '%')
	{
	    (*func) (*fmt++);
	    count++;
	    continue;
	}

	/* ------------------------------------------------------- */
	/* Handle format specification: % [flags] [width] [l] type */
	/* ------------------------------------------------------- */

	/* ----------------- */
	/* 1. Handle [flags] */
	/* ----------------- */

	while ((c = *fmt) == '+' || c == '-')
	{
	   switch (c)
	   {
	      case '+': sign = '+';
			break;

	      case '-': leftjust = 1;
	   }

	   fmt++;
	}

	/* -------------- */
	/* Handle [width] */
	/* -------------- */

	fill = (*fmt == '0') ? *fmt++ : ' ';

	if (*fmt == '*')
	{
	    width = *((int *) ap)++;
	    fmt++;
	}
	else
	    while ('0' <= *fmt && *fmt <= '9')
	       width = width*10+*fmt++-'0';

	/* ---------- */
	/* Handle [l] */
	/* ---------- */

	if (*fmt == 'l')
	{
	    longflag = 1;
	    fmt++;
	}

	/* ----------- */
	/* Handle type */
	/* ----------- */

	str = string;

	switch (*fmt++)
	{
	   case 'c' : string [0] = *(int *) ap;
		      string [1] = '\0';

		      fill = ' ';

		      ((int *) ap)++;

		      if (sign == '+')
			  sign = ' ';

		      break;

	   case 's' : str = *(char **) ap;

		      fill = ' ';

		      ((char **) ap)++;

		      if (sign == '+')
			  sign = ' ';

		      break;

	   case 'd' :
	   case 'i' : if (longflag)
		      {
			  if (*(long *) ap < 0)
			  {
			      sign = '-';
			      *(long *) ap = -*(long *) ap;
			  }
		      }
		      else
			  if (*(int *) ap < 0)
			  {
			      sign = '-';
			      *(int *) ap = -*(int *) ap;
			  }

	   case 'u' : if (longflag)
		      {
			  digit1 = '\0';
			  while (*(long *) ap < 0)
			  {
			     *(long *) ap -= 1000000000L;
			     ++digit1;
			  }
			  _prtl10 (*(unsigned long *) ap, str);
			  ((unsigned long *) ap)++;
			  str [0] += digit1;
		      }
		      else
		      {
			  _prt10 (*(unsigned *) ap, str);
			  ((unsigned *) ap)++;
		      }

		      break;

	   case 'X' : hexcase = 'U';

	   case 'x' : if (longflag)
		      {
			  _prtl16 (*(unsigned long *) ap, str, hexcase);
			  ((unsigned long *) ap)++;
		      }
		      else
		      {
			  _prt16 (*(unsigned *) ap, str, hexcase);
			  ((unsigned *) ap)++;
		      }

		      if (sign == '+')
			  sign = ' ';
		      break;

	    default : while (*percent)
		      {
			 (*func) (*percent++);
			 count++;
		      }
		      return count;
	}

	for (length = 0; str [length] != '\0'; length++)
	     ;

	if (width < 0 || width > 80)
	    width = 0;

	leading = 0;
	if (width)
	{
	    if (length > width)
		length = width;

	    leading = width-length;

	    if (sign == '-' || sign == '+')
		--leading;
	}

	if ((sign == '-' || sign == '+') && fill == '0')
	{
	    (*func) (sign);
	    count++;
	}

	if (!leftjust)
	    for (i = 0; i < leading; i++)
	    {
		 (*func) (fill);
		 count++;
	    }

	if ((sign == '-' || sign == '+') && fill == ' ')
	{
	    (*func) (sign);
	    count++;
	}

	for (i = 0; i < length; i++)
	{
	     (*func) (str [i]);
	     count++;
	}

	if (leftjust)
	    for (i = 0; i < leading; i++)
	    {
		 (*func) (fill);
		 count++;
	    }
   }
}

void _prt10 (unsigned num, char *str)
{
   int i;
   char temp [6];

   temp [0] = '\0';

   for (i = 1; i <= 5; i++)
   {
	temp [i] = num % 10 + '0';
	num /= 10;
   }

   for (i = 5; temp [i] == '0'; i--)
	;

   if (i == 0)
       i++;

   while (i >= 0)
      *str++ = temp [i--];
}

void _prtl10 (unsigned long num, char *str)
{
   int i;
   char temp [11];

   temp [0] = '\0';

   for (i = 1; i <= 10; i++)
   {
	temp [i] = num % 10 + '0';
	num /= 10;
   }

   for (i = 10; temp [i] == '0'; i--)
	;

   if (i == 0)
       i++;

   while (i >= 0)
      *str++ = temp [i--];
}

void _prt16 (unsigned num, char *str, int hexcase)
{
   int i;
   char temp [5];
   char *hexdigits;
   char *hexdigits1 = "0123456789abcdef";
   char *hexdigits2 = "0123456789ABCDEF";

   temp [0] = '\0';
   hexdigits = (hexcase == 'U') ? hexdigits2 : hexdigits1;

   for (i = 1; i <= 4; i++)
   {
	temp [i] = hexdigits [num & 15];
	num >>= 4;
   }

   for (i = 4; temp [i] == '0'; i--)
	;

   if (!i)
       i++;

   while (i >= 0)
      *str++ = temp [i--];
}

void _prtl16 (unsigned long num, char *str, int hexcase)
{
   int i;
   char temp [9];
   char *hexdigits;
   char *hexdigits1 = "0123456789abcdef";
   char *hexdigits2 = "0123456789ABCDEF";

   temp [0] = '\0';
   hexdigits = (hexcase == 'U') ? hexdigits2 : hexdigits1;

   for (i = 1; i <= 8; i++)
   {
	temp [i] = hexdigits [num & 15];
	num >>= 4;
   }

   for (i = 8; temp [i] == '0'; i--)
	;

   if (!i)
       i++;

   while (i >= 0)
      *str++ = temp [i--];
}