[net.unix-wizards] _print/_doprnt; curses on sys III

barb@hpfcms.UUCP (06/14/84)

There are two different source files shipped for printf with System III.
One version of printf()  uses doprnt.  doprnt is written in vax assembly
language.  The  second  version  of  printf()  uses  _print.  _print  is
written  in C.  Thus, the  HP9000  series  500 and  series  200 used the
source for _print.

The  arguments  to _print  are  _print(format,  args).  format is a char
pointer to the format  string and args is a char  pointer to the list of
arguments to be printed.  A global  variable _pfile (FILE *) must be set
to the stream pointer  associated  with the file to be written on before
calling _print.

System  V.2 now uses  only  doprnt  for  printf().  They ship both a vax
assembly  language  version  and a C  version.  The 4.0  release  of the
Series 500 and 2.1 release of the Series 200 (both to be  released  late
this summer) will use System V.2 doprnt.

_print and  doprint are not valid user entry points and  are explicitly 
undocumented  both by HP9000 and by Bell because  their interface is 
subject to change in future releases.


Barbara Flahive, Technical Support
Hewlett-Packard  -  Ft.Collins, CO
{hplabs,ihnp4}!hpfcla!barb

toby@gargoyle.UChicago.UUCP (Toby Harness) (06/15/84)

I am moving some code from a VAX/750 (4.2bsd) to a HP9000 (mostly(!) sys III),
and have discovered that in /lib/libc.a _doprnt has been replaced with _print.
Is this a HP mod or standard sys III?  Does anyone (hplabs??) know how _print
is supposed to work (e.g. type and ordering of arguments)?  We have only
binary from HP on this, and of course _print isn`t documented anywhere.  We
do have source for 4.2bsd and v7.  The problem first came up in curses, but
_doprnt is also used in csh and a couple of other places I can`t recall right
now.  The piece of curses code in question is (from _sprintw in printw.c):

...
char 	*fmt;
int	*args; {

	FILE	junk;
	char	buf[512];

	junk._flag = _IOWRT + _IOSTRG;
	junk._prt = buf;
	junk._cnt = 32767;
	_doprnt(fmt, arg, &junk);
	...

I don`t know, but doesn`t this strike some of you as UGLY?  (comments?)

With a work-around that can`t handle varargs (and that I have too much self
respect to post here) I have curses working just fine on the HP9000 (incl.
Mark Davoren`s recent scroll fixes).  If anyone (with 4.2bsd source, alas)
is interrest in a "#ifdef SYSIII" version of curses, I will be happy to 
send it to him/her, but only *after* I get an answer about _print.

Toby Harness		Ogburn/Stouffer Center, University of Chicago
			...inhp4!gargoyle!sam!toby

geoff@utcsstat.UUCP (Geoff Collyer) (06/16/84)

Any program that calls _doprnt (or _print or whatever) directly is
*broken*.  The internals of a given stdio implementation are the
business of no one but its author(s) and maintainer(s).  If you think
you need to call _doprnt or whatever, try sprintf'ing into a buffer and
passing the buffer.

Many people, especially those at Berkeley, seem to think that there is
only one stdio implementation, Dennis Ritchie's.  It ain't so, folks.
I know of one stdio implementation in progress in which *all* the
internal names, including names in stdio.h that aren't documented, are
different or static so as to forcibly break programs that use the
internal names.

Some people may think that deliberately changing the names hurts
portability; on the contrary, demonstrating that broken programs really
are broken is a favour to their authors.  The authors can fix their
programs before they reach a wider audience and are a greater
embarrassment than they are today.

dave@utcsrgv.UUCP (Dave Sherman) (06/17/84)

In article <1973@utcsstat.UUCP> geoff@utcsstat.UUCP (Geoff Collyer) writes:
~| Any program that calls _doprnt (or _print or whatever) directly is
~| *broken*.  The internals of a given stdio implementation are the
~| business of no one but its author(s) and maintainer(s).  If you think
~| you need to call _doprnt or whatever, try sprintf'ing into a buffer and
~| passing the buffer.
~| 
Geoff, you would be correct to call such programs "non-portable".
I think "broken" is going a little too far. I have some implementations
which "work" just fine.

sprintf'ing into a buffer isn't always the answer. For one, it
requires two or more lines of code for what is conceptually one
action. Secondly, it may not work for the particular need. In
my code which interposes termcap interpretation at the output
level, for example, "putc" is redefined. For this to work, _strout
(called by _doprnt which is called by printf) has to be recompiled
with "putc" #undef'ed from being a macro.

Sure it's non-portable, when I next hit a system which uses some
other stdio. When the time comes I'll make whatever trivial changes
are needed to make it run on such a system. So far I've ported it
from PDP-11 (v7) to a Perkin-Elmer 3220 and an IBM-PC running
Venix/86, and it runs like a charm. So don't tell me it's "broken".

(If you persist, I will define my programs as "written for v7 UNIX"
rather than "written for UNIX". So there.)

Dave Sherman
Toronto
-- 
 {allegra,cornell,decvax,ihnp4,linus,utzoo}!utcsrgv!dave

stan@RICE.ARPA (06/17/84)

From:  Stan Hanks <stan@RICE.ARPA>

Toby,

It is standard System III. AT&T finally wised up to how much software
broke when they took _doprnt away, and made it come back in System V.
Not that this helps you any, but.....

Anyway, here's how you work this:

1) the FILE * that you are printing to is not in the arguments to
   _print. Instead, this goes in the external variable "_pfile".

2) the first argument to _print is the format, followed by the various
   arguments

Therefore the "_doprnt(fmt, args, file)" becomes 
"_pfile = file; _print(fmt, args)" No sweat.


				Stan Hanks
				Department of Computer Science
				Rice University
				Houston TX
				
				stan@rice.ARPA         (arpanet)
				stan@rice              (csnet)
				...!lbl-csam!rice!stan (uucp)

ksbszabo@wateng.UUCP (Kevin S. B. Szabo) (06/18/84)

In article <1973@utcsstat.UUCP> geoff@utcsstat.UUCP (Geoff Collyer) writes:
~| Any program that calls _doprnt (or _print or whatever) directly is
~| *broken*.  The internals of a given stdio implementation are the
~| business of no one but its author(s) and maintainer(s). 

And Dave Sherman replies:
~|Geoff, you would be correct to call such programs "non-portable".
~|I think "broken" is going a little too far. I have some implementations
~|which "work" just fine.
~|sprintf'ing into a buffer isn't always the answer. For one, it
~|requires two or more lines of code for what is conceptually one
~|action.

Dave, I'm sorry but I and many others (I hope), don't agree. The program
implementation is worse that unportable, it is unmaintainable. Sure,
the author can maintain it and will probably continue to keep it humming
perfectly on many systems. But if he/she ever leaves and someone has to
a) fix the code because of an internal, supposedly transparent change to stdio;
or b) port the code to a machine with a totally reworked stdio; the 
maintainer will have to spend many unecessary hours trying to find all the
hidden dependancies on an undocumented internal system routine. Besides, two
lines of code is a small price to pay for clarity, portability and
*maintainability*.

All this from someone who wrote in SP-1 ESS* assembler for a year. Yuck, talk
about un-maintainable.

*SP-1 ESS (Stored Program One, Electronic Switching System. Designed by
Bell-Northern Research circa 1967. All programs in assembler, at least
100-200 thousand lines per version).
-- 
	Kevin Szabo  watmath!wateng!ksbszabo (Elec Eng, U of Waterloo)

dave@utcsrgv.UUCP (Dave Sherman) (06/18/84)

In article <1101@wateng.UUCP> ksbszabo@wateng.UUCP (Kevin S. B. Szabo) writes:
~| Dave, I'm sorry but I and many others (I hope), don't agree. The program
~| implementation is worse that unportable, it is unmaintainable. Sure,
~| the author can maintain it and will probably continue to keep it humming
~| perfectly on many systems. But if he/she ever leaves and someone has to
~| a) fix the code because of an internal, supposedly transparent change to stdio;
~| or b) port the code to a machine with a totally reworked stdio; the 
~| maintainer will have to spend many unecessary hours trying to find all the
~| hidden dependancies on an undocumented internal system routine. Besides, two
~| lines of code is a small price to pay for clarity, portability and
~| *maintainability*.

Two points:

1. Good documentation will avoid the portability problems. I am clearly
documenting in my code exactly what the v7-stdio dependency is.

2. "two lines of code is a small price to pay..."? Not when two lines
would have to be used for every printf in a source file which has a
lot of printfs, escpecially during the development phase.

Dave Sherman
-- 
 {allegra,cornell,decvax,ihnp4,linus,utzoo}!utcsrgv!dave

bob@SU-SHASTA.ARPA (06/18/84)

SHAME ON Ken Arnold and Bill Joy  for  making  assumptions  about
printf's  implementation.  Yes, System III and System V have done
away with _doprnt.  I have re-written printw and wprintw  to  not
have implementation dependencies and reproduce them here for your
use.
--------------------------------------------------------------------------
/*
 *	This routine implements a printf on the standard screen.
 */
printw(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
char	*fmt;
int	*a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a10;
{

	return wprintw(stdscr, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
}

/*
 *	This routine actually executes the printf and adds it to the window
 *	This is really a modified version of "sprintf".  
 *
 */

int
wprintw(win, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
WINDOW *win;
char *fmt;
int *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a10;
{
	int rc;
	char buf[512];

	rc = sprintf(buf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
	return waddstr(win, buf);
}
--------------------------------------------------------------------------
Bob Toxen
Silicon Graphics
ucbvax!Shasta!olympus!bob

geoff@utcsstat.UUCP (Geoff Collyer) (06/18/84)

Programs that rely on the internals of a given stdio implementation are
certainly grossly non-portable, but more importantly they are
*BROKEN*.  By using _doprnt, you are assuming that there exists a
function in every stdio implementation similar to _doprnt in Dennis
Ritchie's implementation.  If such a function exists, it may be static
and thus inaccessible, but it may not even exist.

Defending existing programs on the grounds that they seem to work
doesn't stand up to a moment's reflection; plenty of buggy programs
have appeared to work due to flukes: the v7 mv called strcat to append
to an uninitialised auto char array.  Such behaviour was clearly a bug
and deserved to be fixed; mv was broken, not just unportable.

Sometimes correct code (using sprintf) is a bit longer than incorrect
code (using stdio internals).  Redeclaration of putc is ``perilous''
(stdio(3S)).  I fail to see why putc needs to be redefined.

toby@gargoyle.UChicago.UUCP (Toby Harness) (06/19/84)

Geoff Collyer:
> Any program that calls _doprnt (or _print or whatever) directly is
> *broken*.  The internals of a given stdio implementation are the
> business of no one but its author(s) and maintainer(s).  If you think
> you need to call _doprnt or whatever, try sprintf'ing into a buffer and
> passing the buffer.

I agree, avoiding INTERNALS (gee, what is that name supposed to imply?)
like "_print or whatever" is a good thing, and I use sprintf whenever I
need to print to a buffer.  In fact, until I started poking through some
of the unix source, it never occurred to me to make direct calls to
_doprnt/_print (somehow I got it into my head that that was a no-no). 
But there is a difference between writting a program and maintaining it,
especially when it is not your code to begin with.

> Some people may think that deliberately changing the names hurts
> portability; on the contrary, demonstrating that broken programs really
> are broken is a favour to their authors.  The authors can fix their
> programs before they reach a wider audience and are a greater
> embarrassment than they are today.

Embarrassment? yes.  My opinion of Ken Arnold (for what it`s worth) went 
down a notch when I uncovered that crud in _sprintw.  But I doubt that
"deliberately changing the names ... is a favour (sic) to their authors".

Dave Sherman:
> Sure it's non-portable, when I next hit a system which uses some
> other stdio. When the time comes I'll make whatever trivial changes
> are needed to make it run on such a system. So far I've ported it
> from PDP-11 (v7) to a Perkin-Elmer 3220 and an IBM-PC running
> Venix/86, and it runs like a charm. So don't tell me it's "broken".
> 
> (If you persist, I will define my programs as "written for v7 UNIX"
> rather than "written for UNIX". So there.)


Dennis Ritchie (_The Standard I/O Library_):
	The standard I/O library was designed with the following goals
	in mind....

	2.  It must be simple to use, and also free of the magic numbers
	and mysterious calls whose use mars the understandability and 
	portability of many programs using older packages.

	3.  The interface provided should be applicable on all machines,
	whether or not the programs which implement it are directly portable
	to other systems, or to machines other than the PDP-11 running a
	version of UNIX.



Toby Harness		Ogburn/Stouffer Center, University of Chicago
			...inhp4!gargoyle!toby

smk@axiom.UUCP (Steven M. Kramer) (06/20/84)

When I was porting some programs/packages, I ran again the _doprnt()
problem in curses and other things.  I wasn't lucky enough to
	1) have it work OK, or
	2) have it exist.
What had happened was that 2 arguments in _doprnt (which was defined
in both UNIX systems I was working with) were reversed.  Since I never
expected this and had other porting problems at the time, it took
a long time to find this gem.

Now, the authors of each _doprnt had every right to do their own implementation
of what is essentially a part of printf.  There were no standards on _doprnt,
nor were there even any man pages on it or references to it (except this
has changed in 4.2).  ... and since there were no standards, it shouldn't
have been used except as a way to implement printf.

Maybe it's time in the next releases of UNIX to declare all support routines
in the UNIX support libraries as static so that external references like
that won't happen.
-- 
	--steve kramer
	{allegra,genrad,ihnp4,utzoo,philabs,uw-beaver}!linus!axiom!smk	(UUCP)
	linus!axiom!smk@mitre-bedford					(MIL)

dhb@rayssd.UUCP (06/20/84)

If you think that trying to bring up curses (or similar program which
use _doprnt) is a problem on sys III, you should try bringing it up
on VMS using the VAX11C compiler from DEC.  Not only did they change
the internals of the 'stdio' package, but they don't tell you what
they are.  Since we don't have source for VMS (does anybody?), I was
forced to rewrite the printw routines in order to get them to work.

I think that anyone who is writing code that will end up in the public
domain should be careful to avoid any system dependencies.  Given the
fact that VMS now has a reasonably good implementation of the C compiler,
more and more packages are going to start popping up on VMS machines.
Most commercial outfits are already aware of this and take care to make
sure their code is not system dependent (if possible).
--
	Dave Brierley
	Raytheon Co.; Portsmouth RI; (401)-847-8000 x4073
	...!decvax!brunix!rayssd!dhb
	...!allegra!rayssd!dhb
	...!linus!rayssd!dhb

rcc@imsvax.UUCP (06/21/84)

If you're adamant about speed, efficiency, etc., then do what ex does,
don't use stdio.  However, if you're going to use stdio, then you
should *not* use any undocumented/internal routines and claim that
the program is portable, even if you document the fact that you used
them.  Documenting that your program is dependent on multiplexed files
doesn't make it portable if a system doesn't have multiplexed
files (at least, not without some work).  Either include your own 
stdio in the program that works, or play by the rules.  Doing 
otherwise guarantees that your program will break.  The only 
question is when.
-- 

The preceding message was brought to you by --

		Ray Chen

UUCP:	umcp-cs!eneevax!imsvax!rcc  (NEW ADDRESS)
Voice:	(301)  984-8343
USnail:	Integrated Microcomputer Systems, Inc.
	Suite 400
	6100 Executive Blvd.
	Rockville, MD  20852

henry@utzoo.UUCP (Henry Spencer) (06/21/84)

Alas, Bob Toxen's rewrites of printw and wprintw continue to have
implementation dependencies:  the declare-lots-of-arguments trick
depends on the stack frame being organized a certain way.

Before we get started on a big discussion about varargs functions,
let me point out that this was discussed at excruciating length a
few months ago (possibly in another newsgroup, I forget), and the
conclusion was quite simple:

	There is *no* fully machine-independent way to write a
	function which takes a variable number of arguments, even
	if all the function is trying to do is to pass its argument
	list on to another function.

Not even the varargs.h machinery is workable on all machines, and
the various more simplistic schemes are much less portable.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

hansen@pegasus.UUCP (Tony L. Hansen) (06/22/84)

> > It is standard System III. AT&T finally wised up to how much software
> > broke when they took _doprnt away, and made it come back in System V.
> Well, I looked, and "_doprnt" is back; in S5R2, they also provide the
> "vprintf", "vfprintf", and "vsprintf" interfaces that were in S3 as
> distributed (and which can be used instead of "_doprnt"), but were
> ripped out of S5.  "_doprnt" is, alas, not in every implementation of
> standard I/O, so software which uses it will still break under some
> implementations.  

Still wrong! _doprnt() is only in SOME versions of System V. I have
worked with at least three different System V machines that did NOT
have _doprnt() in their libc. Nor did they have _print().  The Vax
version of System V DOES have _doprnt() in it for System V, but don't
count on it being there forever.

How to get around it? We've made extensive use of a C version of
_doprnt(), as well as the v*printf() family. There is a version of
v*printf() which works with _doprnt(). (Our curses uses vsprintf().)
In System Vr2, v*printf() is implemented as a supported part of the
stdio package and uses whatever underlying mechanism the rest of
stdio uses.

I wish BSD had picked up the v*printf() functions rather than
documenting the _doprnt() routine. Re-implementing v*printf() would
be a 15 minute job for someone who knows _doprnt() well.

					Tony Hansen
					pegasus!hansen

guy@rlgvax.UUCP (Guy Harris) (06/23/84)

> Gee guys ... I wouild like to see you implement something
> like printw *correctly* without calling _doprnt.  I do NOT
> consider limiting the number of format items to 10 (or any
> other upper limit) a correct implementation.

Unfortunately, on some systems you can't implement "printw" correctly by
calling "_doprnt", because it doesn't exist; on other systems, I believe
"_doprnt" may have a different calling sequence than the "standard" one.
True, the other implementations don't work in the general case, but it's a
choice between one that works with *most* C implementations (there probably
isn't one that works on *all* implementations) but has limitations, vs.
one that works only on some C/"stdio" implementations but doesn't have those
limitations.

> However (and here's the kicker), _doprnt IS documented
> in 4.2BSD!!!  Look at the manual page for printf(3S) if you
> doubt me. ... I don't have Sys III documentation in front of me,
> but, is _print documented in Sys III?  I suspect that it is.

"_print" isn't documented in System III.  "vprintf", "vfprintf", and "vsprintf",
unfortunately, aren't documented either; they were intended to be the "visible"
hook for routines that need things like "_doprnt".  System V doesn't document
them, but that's because they were removed.  System V Release 2 put them
back in, *and* put "_doprnt" back in, and documented the "vprintf" family
(although *not* "_doprnt"):

     NAME
          vprintf, vfprintf, vsprintf - print formatted output of a
          varargs argument list

     SYNOPSIS
          #include <stdio.h>
          #include <varargs.h>

          int vprintf (format, ap)
          char *format;
          va_list ap;

          int vfprintf (stream, format, ap)
          FILE *stream;
          char *format;
          va_list ap;

          int vsprintf (s, format, ap)
          char *s, *format;
          va_list ap;

     DESCRIPTION
          vprintf, vfprintf, and vsprintf are the same as printf,
          fprintf, and sprintf respectively, except that instead of
          being called with a variable number of arguments, they are
          called with an argument list as defined by varargs(5).

     EXAMPLE
          The following demonstrates how vfprintf could be used to
          write an error routine.

          #include <stdio.h>
          #include <varargs.h>
               .
               .
               .
          /*
           *   error should be called like
           *        error(function_name, format, arg1, arg2...);
           */
          /*VARARGS0*/
          void
          error(va_alist)
          /* Note that the function_name and format arguments cannot be
           *      separately declared because of the definition of varargs.
           */
          va_dcl
          {
               va_list args;
               char *fmt;

               va_start(args);
               /* print out name of function causing error */
               (void)fprintf(stderr, "ERROR in %s: ", va_arg(args, char *));
               fmt = va_arg(args, char *);
               /* print out remainder of message */
               (void)vfprintf(fmt, args);
               va_end(args);
               (void)abort( );
          }

Unfortunately, this still doesn't give a portable way (even between "similar"
C implementations) for doing "printw"-like functions.  You have:

	PDP-11 V7 - use "_doprnt".
	Other V7 - try using "_doprnt", and hope that it exists and has
	the same implementation.
	4.xBSD - use "_doprnt".
	System III (PDP-11, VAX-11, and other ones that adopted one or the
	   other of those implementations) - use "v*printf".
	System V - suffer, unless they revived "_doprnt".
	System V Release 2 (VAX-11 and other ones that adopted that
	   implementation, and possibly PDP-11 as well) - use "v*printf"
	   (the preferred, clean way; it's compatible with System III) or
	   "_doprnt" (if you want to be compatible with one using a compatible
	   "_doprnt" which doesn't provide "v*printf".

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy

guy@rlgvax.UUCP (Guy Harris) (06/26/84)

> It is standard System III. AT&T finally wised up to how much software
> broke when they took _doprnt away, and made it come back in System V.
> Not that this helps you any, but.....

Well, I looked, and "_doprnt" is back; in S5R2, they also provide the "vprintf",
"vfprintf", and "vsprintf" interfaces that were in S3 as distributed (and which
can be used instead of "_doprnt"), but were ripped out of S5.  "_doprnt" is,
alas, not in every implementation of standard I/O, so software which uses
it will still break under some implementations.  Alas, there is no portable
way to write routines which take "printf"-like call interfaces; neither
"v*printf" nor "_doprnt" are guaranteed to be there ("v*printf" were in the
distributed S3, but not documented).

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy

rcd@opus.UUCP (Dick Dunn) (06/27/84)

Dave Sherman is defending his use of the internal functions _print/_doprint
in his code:

>1. Good documentation will avoid the portability problems. I am clearly
>documenting in my code exactly what the v7-stdio dependency is.

How many times have I overheard someone discussing a disastrous bug when
some gadfly comes up, cackles "So document it and call it a feature!" and
goes laughing off into the distance.  Documentation doesn't avoid the
portability problems, it only points out where they are.  Well-documented
bad practice is still bad practice.

>2. "two lines of code is a small price to pay..."? Not when two lines
>would have to be used for every printf in a source file which has a
>lot of printfs, escpecially during the development phase.

So use a procedure or a macro to encapsulate the two lines.  That should be
obvious - performance, to the tune of an extra procedure call or so, is
hardly an issue when printf is at the business end.

I think (hope) that the effect of this discussion will be to convince Dave
that what he's doing is "bad practice" such as the collective conscience of
the net might define it.
-- 
Dick Dunn	{hao,ucbvax,allegra}!nbires!rcd		(303)444-5710 x3086
	...Cerebus for dictator!

hansen@pegasus.UUCP (Tony L. Hansen) (07/06/84)

Golly, no one bit after two weeks.

> Gee guys ... I wouild like to see you implement something
> like printw *correctly* without calling _doprnt.  I do NOT
> consider limiting the number of format items to 10 (or any
> other upper limit) a correct implementation.

I'd be glad to, using the v*printf() facility now fully documented and
supported as part of System Vr2:

	#include <stdio.h>
	#include <varargs.h>

	printw (va_alist)
	va_dcl
	{
	    va_list ap;
	    WINDOW *win;
	    char *fmt;
	    char buffer[BUFSIZ];

	    va_start(ap);
	    win = va_arg(ap, WINDOW *);
	    fmt = va_arg(ap,char *);
	    vsprintf(buffer, fmt, ap);
	    va_end(ap);
	    return waddstr(win,buffer);
	}

I think that that's as portable an implementation as you're going to get,
assuming that you have varargs.h on your system, modulo Henry Spencer's
comments on varargs.h:

> Not even the varargs.h machinery is workable on all machines, and
> the various more simplistic schemes are much less portable.


The REAL kicker is that you need vsprintf(). In System Vr2, it is
implemented using _doprnt on the Vaxen, using an assembly language routine
(not called _doprnt) on the AT&T 3b20, and I don't know what else on the
other machines that System Vr2 is available on. The point is that is to be
implemented using whatever underlying mechanism is available for the rest of
your stdio implementation.

So, if you don't have v*printf() on your system, here is a version of
vsprintf() that should work with the _doprnt() that comes with 4.xBSD, V7
and System V, rewritten from some public domain software that I had lying
around:

	#include <stdio.h>
	#ifdef BSD
	# define MAXINT 32767
	#else
	# include <values.h>
	#endif BSD
	#include <varargs.h>

	vsprintf(buffer, fmt, ap)
	char *buffer, *fmt;
	va_list ap;
	{
	    FILE temp;
	    int count;
	    extern int _doprnt();

	    temp._cnt = MAXINT;
	#ifdef BSD
	    temp._flag = _IOSTRG | _IOWRT;
	    temp._ptr = buffer;
	#else
	    temp._base = temp._ptr = (unsigned char *) buffer;
	    temp._flag = _IOWRT;
	#endif BSD
	    temp._file = _NFILE;
	    count = _doprnt(fmt, ap, &temp);
	    *temp._ptr = '\0';
	    return count;
	}

It shouldn't be too hard to rewrite the above to use _print() or whatever
other internal mechanism your stdio happens to use. Vprintf() and vfprintf()
are left as an exercize for the reader. (Does anyone want to do a completely
public domain version for the record?)

There is now no excuse for anyone to have to reinvent the wheel as long as
people start providing the v*printf() facilities along with the rest of
their stdio implementations.

					Tony Hansen
					pegasus!hansen