/* Portable vfprintf and vprintf by Robert A. Larson <blarson@skat.usc.edu> */

/* Copyright 1989 Robert A. Larson.
 * Distribution in any form is allowed as long as the author
 * retains credit, changes are noted by their author, and the
 * copyright message remains intact.  This program comes as-is
 * with no warranty of fitness for any purpose.
 *
 * Thanks to Doug Gwen, Chris Torek, and others who helped clarify
 * the ANSI printf specs.
 *
 * Please send any bug fixes and improvements to blarson@skat.usc.edu .
 * The use of goto is NOT a bug.
 */

/* Feb	9, 1989		blarson		First usenet release */

/* This code implements the vfprintf function, without relying on
 * the existance of _doprnt or other system specific code.
 *
 * Define NOVOID if (void *) is not a supported type.
 *
 * Two compile options are available for efficency:
 *	INTSPRINTF	should be defined if sprintf is int and returns
 *				the number of chacters formatted.
 *	LONGINT		should be defined if sizeof(long) == sizeof(int)
 *
 * They only make the code smaller and faster, they need not be defined.
 *
 * UNSIGNEDSPECIAL should be defined if unsigned is treated differently
 * than int in argument passing.  If this is defined, and LONGINT is not,
 * the compiler must support the type unsigned long.
 *
 * Most quirks and bugs of the available fprintf fuction are duplicated,
 * however "*" in the width and precision fields will work correctly
 * even if fprintf does not support this.  The %n format and the return
 * count will only work if fprintf returns the number of characters
 * formatted.
 *
 * Bad format strings, or those with very long width and precision
 * fields (including expanded * fields) will cause undesired results.
 */

#define ULONGINT	unsigned long

#ifdef OSK		/* os9/68k can take advantage of both */
#  define INTSPRINTF
#  define LONGINT
#  undef  ULONG
#  define ULONG	unsigned int
#endif /* OSK */

#ifdef MCH_AMIGA	/* Manx looks pretty much (!?) like Lattice */
#  define	INTSPRINTF
#  undef	ULONG
#  define	ULONG	unsigned long
#  if sizeof(long) == sizeof(int)
#    define	LONGINT	/* Defined if (long) == (int) */
#  endif
#endif

#ifdef AMIGA		/* Lattice can take advantage of both */
#  define INTSPRINTF
#  if sizeof(long) == sizeof(int)
#    define LONGINT
#  endif
#  undef  ULONG
#  define ULONG	unsigned long
#endif /* AMIGA */

#ifdef NOVOID
typedef char *pointer;
#else
typedef void *pointer;
#endif /* NOVOID */

#include <stdio.h>

#ifdef	INTSPRINTF
#  define Sprintf(string,format,arg)	(sprintf((string),(format),(arg)))
#else
#  define Sprintf(string,format,arg)	(\
	sprintf((string),(format),(arg)),\
	strlen(string)\
)
#endif /* INTSPRINTF */

#ifdef __STDC__
#  include <stdarg.h>
#else
#  include "varargs.h"
#endif /* __STDC__ */

typedef int *intp;

int vfprintf(dest, format, args)
FILE *dest;
register char *format;
va_list args;
{
    register char c;
    register char *tp;
    register int count = 0;
    char tempfmt[64];

    tempfmt[0] = '%';
    while (c = *format++) {
		if (c == '%') {
		    tp = &tempfmt[1];
continue_format:
		    switch(c = *format++) {
			case 's':
			    *tp++ = c;
			    *tp = '\0';
			    count += fprintf(dest, tempfmt, va_arg(args, char *));
			    break;
			case 'u':
			case 'x':
			case 'o':
			case 'X':
			case 'd':
			case 'c':
			case 'i':
			    *tp++ = c;
			    *tp = '\0';
#ifndef LONGINT
				if (tempfmt[1] == 'l')
					count += fprintf(dest, tempfmt, va_arg(args, ULONG));
				else
#endif /* LONGINT */
					count += fprintf(dest, tempfmt, va_arg(args, int));
			    break;
			case 'f':
			case 'e':
			case 'E':
			case 'g':
			case 'G':
			    *tp++ = c;
			    *tp = '\0';
			    count += fprintf(dest, tempfmt, va_arg(args, double));
			    break;
			case 'p':
			    *tp++ = c;
			    *tp = '\0';
			    count += fprintf(dest, tempfmt, va_arg(args, pointer));
			    break;
			case '-':
			case '+':
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '.':
			case ' ':
			case '#':
			case 'h':
#ifndef LONGINT
			case 'l':
			    *tp++ = c;
				/* FALLTHROUGH */
#else /* !LONGINT */
			    *tp++ = c;
				/* FALLTHROUGH */
			case 'l':
#endif /* LONGINT */
			    goto continue_format;
			case '*':
			    tp += Sprintf(tp, "%d", va_arg(args, int));
			    goto continue_format;
			case 'n':
			    *va_arg(args, intp) = count;
			    break;
			case '%':
			default:
			    putc(c, dest);
			    count++;
			    break;
		    }
		} else {
		    putc(c, dest);
		    count++;
		}
    }
    return( count );
}

vprintf(format, args)
char *format;
va_list args;
{
    return( vfprintf(stdout, format, args) );
}
