Portable mini-vfprintf?

Jef Poskanzer jef at well.sf.ca.us
Sun Jun 30 13:11:46 AEST 1991


In the referenced message, torek at elf.ee.lbl.gov (Chris Torek) wrote:
}Let me clarify a bit:  I suggested using vfprintf where it
}exists, and where it does not, falling back on _doprnt.

Yeah, my current version has two fallbacks.  If you get a link
error on vfprintf, you define NEED_VFPRINTF1, which gives you
a vfprintf that just calls _doprnt.  If you then get a link
error on _doprnt, you define NEED_VFPRINTF2 and you get the
portable mini-vfprintf.

To avoid using gcvt, how about writing vfprintf in terms of fprintf?
Have it parse the format string into individual format specifiers, do
the appropriate va_arg call, and then fprintf each arg individually.
It would be silly to organize a general-purpose stdio that way, but it's
also silly to not have vfprintf.  Fight silly with silly!  Code appended.
---
Jef

  Jef Poskanzer  jef at well.sf.ca.us  {apple, ucbvax, hplabs}!well!jef
                     WCBG: All Elvis, All The Time

#ifdef NEED_VFPRINTF1

/* Micro-vfprintf, for systems that don't have vfprintf but do have _doprnt.
*/

int
vfprintf( stream, format, args )
    FILE* stream;
    char* format;
    va_list args;
    {
    return _doprnt( format, args, stream );
    }
#endif /*NEED_VFPRINTF1*/

#ifndef NEED_VFPRINTF2

/* Portable mini-vfprintf, for systems that don't have either vfprintf or
** _doprnt.  This depends only on fprintf.  If you don't have fprintf,
** you might consider getting a new stdio library.
*/

int
vfprintf( stream, format, args )
    FILE* stream;
    char* format;
    va_list args;
    {
    char* ep;
    char fchar;
    char tformat[512];
    int do_long;
    int i;
    long l;
    unsigned long ul;
    char* s;
    double d;

    while ( *format != '\0' )
	{
	if ( *format != '%' )
	    { /* Not special, just write out the char. */
	    putc( *format, stream );
	    ++format;
	    }
	else
	    {
	    do_long = 0;
	    ep = format + 1;

	    /* Skip over all the field width and precision junk. */
	    if ( *ep == '-' )
		++ep;
	    if ( *ep == '0' )
		++ep;
	    while ( isdigit( *ep ) )
		++ep;
	    if ( *ep == '.' )
		{
		++ep;
		while ( isdigit( *ep ) )
		    ++ep;
		}
	    if ( *ep == '#' )
		++ep;
	    if ( *ep == 'l' )
		{
		do_long = 1;
		++ep;
		}

	    /* Here's the field type.  Extract it, and copy this format
	    ** specifier to a temp string so we can add an end-of-string.
	    */
	    fchar = *ep;
	    (void) strncpy( tformat, format, ep - format + 1 );
	    tformat[ep - format + 1] = '\0';

	    /* Now do a one-argument fprintf with the format string we have
	    ** isolated.
	    */
	    switch ( fchar )
		{
		case 'd':
		if ( do_long )
		    l = va_arg( args, long );
		else
		    l = (long) ( va_arg( args, int ) );
		(void) fprintf( stream, tformat, l );
		break;

	        case 'o':
	        case 'x':
	        case 'u':
		if ( do_long )
		    ul = va_arg( args, unsigned long );
		else
		    ul = (unsigned long) ( va_arg( args, unsigned ) );
		(void) fprintf( stream, tformat, ul );
		break;

	        case 'c':
		i = (char) va_arg( args, int );
		(void) fprintf( stream, tformat, i );
		break;

	        case 's':
		s = va_arg( args, char* );
		(void) fprintf( stream, tformat, s );
		break;

	        case 'e':
	        case 'f':
	        case 'g':
		d = va_arg( args, double );
		(void) fprintf( stream, tformat, d );
		break;

	        case '%':
		putc( '%', stream );
		break;

		default:
		return -1;
		}

	    /* Resume formatting on the next character. */
	    format = ep + 1;
	    }
	}
    return 0;
    }
#endif /*NEED_VFPRINTF2*/



More information about the Comp.lang.c mailing list