[alt.sources.wanted] Portable mini-vfprintf?

jef@well.sf.ca.us (Jef Poskanzer) (06/29/91)

I have this large software package, pbmplus, which I would like to be
as portable as possible.  It has a unified message and error routine
called pm_message, which really ought to be a varargs routine.  The
problem is doing this portably.  I can deal with varargs.h vs. stdarg.h
easily enough, but what about the substantial minority of systems out
there which don't have vfprintf?

The proposed skeleton of the portable version is appended.  If you can
think of a way to do this without needing to supply a vfprintf, I'd
love to hear about it.  Otherwise, do you have a vfprintf lying around?
---
Jef

  Jef Poskanzer  jef@well.sf.ca.us  {apple, ucbvax, hplabs}!well!jef
                               "Swell."

#if __STDC__
static void
pm_message( char* fmt, ... )
    {
    va_list ap;

    va_start( ap, fmt );
#else /*__STDC__*/
/*VARARGS1*/
static void
pm_message( va_alist )
    va_dcl
    {
    va_list ap;
    char* fmt;

    va_start(ap);
    fmt = va_arg( ap, char* );
#endif /*__STDC__*/

    (void) vfprintf( stderr, fmt, ap );
    va_end( ap );
    }

#ifdef NEED_VFPRINTF
    /* portable mini-vfprintf goes here */
#endif /*NEED_VFPRINTF*/

jef@well.sf.ca.us (Jef Poskanzer) (06/29/91)

In the referenced message, jef@well.sf.ca.us (Jef Poskanzer) wrote:
}do you have a vfprintf lying around?

Thanks to a prompt mail responder, I found a mini-printf in the version
of tcsh on uunet.  I'm still interested in opinions on whether this is
the right solution.
---
Jef

  Jef Poskanzer  jef@well.sf.ca.us  {apple, ucbvax, hplabs}!well!jef
 "The great tragedy of Science - the slaying of a beautiful hypothesis
               by an ugly fact." -- Thomas Henry Huxley

jef@well.sf.ca.us (Jef Poskanzer) (06/30/91)

To support floating-point output in the mini-vfprintf, I added a call
to gcvt.  How portable is this?

Chris Torek suggested I just call _doprnt instead of vfprintf - same
args, different order.  How portable is this?

People have gotten the current version of pbmplus working under VMS and
MS-DOS, and I don't want to lose that.
---
Jef
                                   
  Jef Poskanzer  jef@well.sf.ca.us  {apple, ucbvax, hplabs}!well!jef

torek@elf.ee.lbl.gov (Chris Torek) (06/30/91)

In article <25777@well.sf.ca.us> Jef Poskanzer <jef@well.sf.ca.us> writes:
>To support floating-point output in the mini-vfprintf, I added a call
>to gcvt.  How portable is this?

Not.

>Chris Torek suggested I just call _doprnt instead of vfprintf - same
>args, different order.  How portable is this?

Also not.  Let me clarify a bit:  I suggested using vfprintf where it
exists, and where it does not, falling back on _doprnt.  Most `supported'
systems have at least the ANSI C libraries (if not the syntaxes), and
most `unsupported' C systems seem to be aging Unix systems.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

jef@well.sf.ca.us (Jef Poskanzer) (06/30/91)

In the referenced message, torek@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@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*/

jef@well.sf.ca.us (Jef Poskanzer) (06/30/91)

In the referenced message, Jef Poskanzer <jef@well.sf.ca.us> wrote:
}#ifndef NEED_VFPRINTF2

Obviously this should be ifdef.  I had it set the opposite way for testing.
---
Jef
                                   
  Jef Poskanzer  jef@well.sf.ca.us  {apple, ucbvax, hplabs}!well!jef
	"Lots of women.  All Romulan women." -- William Shatner

jef@well.sf.ca.us (Jef Poskanzer) (06/30/91)

Also, this:

}    return _doprnt( format, args, stream );

should, I am informed, be more like this:

#ifdef BSD
    return _doprnt( format, &args, stream );
#else /*BSD*/
    return _doprnt( format, args, stream );
#endif /*BSD*/

Hmm, I didn't realize there were alternate versions of _doprnt kicking around.
Maybe I shouldn't bother with the _doprnt version at all.
---
Jef

  Jef Poskanzer  jef@well.sf.ca.us  {apple, ucbvax, hplabs}!well!jef
               Speak softly and carry a terawatt laser.

mycroft@kropotki.gnu.ai.mit.edu (Charles Hannum) (07/01/91)

In article <25767@well.sf.ca.us> jef@well.sf.ca.us (Jef Poskanzer) writes:

> I have this large software package, pbmplus, which I would like to be
> as portable as possible.  It has a unified message and error routine
> called pm_message, which really ought to be a varargs routine.  The
> problem is doing this portably.  I can deal with varargs.h vs. stdarg.h
> easily enough, but what about the substantial minority of systems out
> there which don't have vfprintf?

This is one of my arguments for varargs in macros.  Wouldn't it be so
nice to:

  #define pm_message(f,x,...) fprintf( stderr, f, ...)


Opinions welcome, flames ignored.