/*
 * This file based on printf.c from 'Dlibs' on the atari ST  (RdeBath)
 *
 * 19-OCT-88: Dale Schumacher
 * > John Stanley has again been a great help in debugging, particularly
 * > with the printf/scanf functions which are his creation.  
 *
 *    Dale Schumacher                         399 Beacon Ave.
 *    (alias: Dalnefre')                      St. Paul, MN  55104
 *    dal@syntel.UUCP                         United States of America
 *  "It's not reality that's important, but how you perceive things."
 *
 */

/* Altered to use stdarg, made the core function vfprintf.
 * Hooked into the stdio package using 'inside information'
 * Altered sizeof() assumptions, now assumes all integers except chars
 * will be either
 *  sizeof(xxx) == sizeof(long) or sizeof(xxx) == sizeof(short)
 *
 * -RDB
 */

#include <sys/types.h>
#include <fcntl.h>
#if defined(__STDC__) && !defined(__FIRST_ARG_IN_AX__)
#include <stdarg.h>
#define va_strt      va_start
#else
#include <varargs.h>
#define va_strt(p,i) va_start(p)
#endif

#include "stdio.h"

#ifdef L_printf

#if defined(__STDC__) && !defined(__FIRST_ARG_IN_AX__)
int printf(const char * fmt, ...)
#else
int printf(fmt, va_alist)
__const char *fmt;
va_dcl
#endif
{
  va_list ptr;
  int rv;
  va_strt(ptr, fmt);
  rv = vfprintf(stdout,fmt,ptr);
  va_end(ptr);
  return rv;
}
#endif

#ifdef L_sprintf
#if defined(__STDC__) && !defined(__FIRST_ARG_IN_AX__)
int sprintf(char * sp, const char * fmt, ...)
#else
int sprintf(sp, fmt, va_alist)
char * sp;
__const char *fmt;
va_dcl
#endif
{
static FILE  string[1] =
{
   {0, 0, (char*)(unsigned) -1, 0, (char*) (unsigned) -1, -1,
    _IOFBF | __MODE_WRITE}
};

  va_list ptr;
  int rv;
  va_strt(ptr, fmt);
  string->bufpos = sp;
  rv = vfprintf(string,fmt,ptr);
  va_end(ptr);
  *(string->bufpos) = 0;
  return rv;
}
#endif

#ifdef L_fprintf
#if defined(__STDC__) && !defined(__FIRST_ARG_IN_AX__)
int fprintf(FILE * fp, const char * fmt, ...)
#else
int fprintf(fp, fmt, va_alist)
FILE * fp;
__const char *fmt;
va_dcl
#endif
{
  va_list ptr;
  int rv;
  va_strt(ptr, fmt);
  rv = vfprintf(fp,fmt,ptr);
  va_end(ptr);
  return rv;
}
#endif

#ifdef L_vprintf
int vprintf(fmt, ap)
__const char *fmt;
va_list ap;
{
  return vfprintf(stdout,fmt,ap);
}
#endif

#ifdef L_vsprintf
int vsprintf(sp, fmt, ap)
char * sp;
__const char *fmt;
va_list ap;
{
static FILE  string[1] =
{
   {0, 0, (char*)(unsigned) -1, 0, (char*) (unsigned) -1, -1,
    _IOFBF | __MODE_WRITE}
};

  int rv;
  string->bufpos = sp;
  rv = vfprintf(string,fmt,ap);
  *(string->bufpos) = 0;
  return rv;
}
#endif

#ifdef L_vfprintf

#ifdef FLOATS
int (*__fp_print)() = 0;
#endif

static int
prtfld(op, buf, ljustf, sign, pad, width, preci, buffer_mode)
register FILE *op;
register unsigned char *buf;
int   ljustf;
register char sign;
char  pad;
register int width;
int   preci;
int   buffer_mode;
/*
 * Output the given field in the manner specified by the arguments. Return
 * the number of characters output.
 */
{
   register int cnt = 0, len;
   register unsigned char ch;

   len = strlen(buf);

   if (*buf == '-')
      sign = *buf++;
   else if (sign)
      len++;

   if ((preci != -1) && (len > preci))	/* limit max data width */
      len = preci;

   if (width < len)		/* flexible field width or width overflow */
      width = len;

   /*
    * at this point: width = total field width len   = actual data width
    * (including possible sign character)
    */
   cnt = width;
   width -= len;

   while (width || len)
   {
      if (!ljustf && width)	/* left padding */
      {
	 if (len && sign && (pad == '0'))
	    goto showsign;
	 ch = pad;
	 --width;
      }
      else if (len)
      {
	 if (sign)
	 {
	  showsign:ch = sign;	/* sign */
	    sign = '\0';
	 }
	 else
	    ch = *buf++;	/* main field */
	 --len;
      }
      else
      {
	 ch = pad;		/* right padding */
	 --width;
      }
      putc(ch, op);
      if( ch == '\n' && buffer_mode == _IOLBF ) fflush(op);
   }

   return (cnt);
}

int
vfprintf(op, fmt, ap)
FILE *op;
register __const char *fmt;
register va_list ap;
{
   register int i, cnt = 0, ljustf, lval;
   int   preci, dpoint, width;
   char  pad, sign, radix, hash;
   register char *ptmp;
   char  tmp[64], *ltostr(), *ultostr();
   int buffer_mode;

   /* This speeds things up a bit for unbuffered */
   buffer_mode = (op->mode&__MODE_BUF);
   op->mode &= (~__MODE_BUF);

   while (*fmt)
   {
      if (*fmt == '%')
      {
         if( buffer_mode == _IONBF ) fflush(op);
	 ljustf = 0;		/* left justify flag */
	 sign = '\0';		/* sign char & status */
	 pad = ' ';		/* justification padding char */
	 width = -1;		/* min field width */
	 dpoint = 0;		/* found decimal point */
	 preci = -1;		/* max data width */
	 radix = 10;		/* number base */
	 ptmp = tmp;		/* pointer to area to print */
	 hash = 0;
	 lval = (sizeof(int)==sizeof(long));	/* long value flaged */
       fmtnxt:
	 i = 0;
	 for(;;)
	 {
	    ++fmt;
	    if(*fmt < '0' || *fmt > '9' ) break;
	    i = (i * 10) + (*fmt - '0');
	    if (dpoint)
	       preci = i;
	    else if (!i && (pad == ' '))
	    {
	       pad = '0';
	       goto fmtnxt;
	    }
	    else
	       width = i;
	 }

	 switch (*fmt)
	 {
	 case '\0':		/* early EOS */
	    --fmt;
	    goto charout;

	 case '-':		/* left justification */
	    ljustf = 1;
	    goto fmtnxt;

	 case ' ':
	 case '+':		/* leading sign flag */
	    sign = *fmt;
	    goto fmtnxt;

	 case '*':		/* parameter width value */
	    i = va_arg(ap, int);
	    if (dpoint)
	       preci = i;
	    else
	       width = i;
	    goto fmtnxt;

	 case '.':		/* secondary width field */
	    dpoint = 1;
	    goto fmtnxt;

	 case 'l':		/* long data */
	    lval = 1;
	    goto fmtnxt;

	 case 'h':		/* short data */
	    lval = 0;
	    goto fmtnxt;

	 case 'd':		/* Signed decimal */
	 case 'i':
	    ptmp = ltostr((long) ((lval)
			 ? va_arg(ap, long)
			 : va_arg(ap, short)),
		 10);
	    goto printit;

	 case 'b':		/* Unsigned binary */
	    radix = 2;
	    goto usproc;

	 case 'o':		/* Unsigned octal */
	    radix = 8;
	    goto usproc;

	 case 'p':		/* Pointer */
	    lval = (sizeof(char*) == sizeof(long));
	    pad = '0';
	    width = 6;
	    preci = 8;
	    /* fall thru */

	 case 'x':		/* Unsigned hexadecimal */
	 case 'X':
	    radix = 16;
	    /* fall thru */

	 case 'u':		/* Unsigned decimal */
	  usproc:
	    ptmp = ultostr((unsigned long) ((lval)
				   ? va_arg(ap, unsigned long)
				   : va_arg(ap, unsigned short)),
		  radix);
	    if( hash && radix == 8 ) { width = strlen(ptmp)+1; pad='0'; }
	    goto printit;

	 case '#':
	    hash=1;
	    goto fmtnxt;

	 case 'c':		/* Character */
	    ptmp[0] = va_arg(ap, int);
	    ptmp[1] = '\0';
	    goto nopad;

	 case 's':		/* String */
	    ptmp = va_arg(ap, char*);
	  nopad:
	    sign = '\0';
	    pad = ' ';
	  printit:
	    cnt += prtfld(op, ptmp, ljustf,
			   sign, pad, width, preci, buffer_mode);
	    break;

#if FLOATS
	 case 'e':		/* float */
	 case 'f':
	 case 'g':
	 case 'E':
	 case 'G':
	    if ( __fp_print )
	    {
	       (*__fp_print)(&va_arg(ap, double), *fmt, preci, ptmp);
	       preci = -1;
	       goto printit;
	    }
	    /* FALLTHROUGH if no floating printf available */
#endif

	 default:		/* unknown character */
	    goto charout;
	 }
      }
      else
      {
       charout:
	 putc(*fmt, op);	/* normal char out */
	 ++cnt;
         if( *fmt == '\n' && buffer_mode == _IOLBF ) fflush(op);
      }
      ++fmt;
   }
   op->mode |= buffer_mode;
   if( buffer_mode == _IONBF ) fflush(op);
   if( buffer_mode == _IOLBF ) op->bufwrite = op->bufstart;
   return (cnt);
}
#endif
