caspers@fwi.uva.nl (B.M. Caspers (I)) (02/26/91)
Does anyone know if there is a good way to call printf from a function prototypes: int my_printf( char *fmt, ... ); I need to write a function that calls the win_printf of the DESQview API C library, which in its turn (I think) calls printf. Since I don't want to do any argument processing that printf should do, can I give the format string and the rest of the arguments unchanged to [win_]printf? Thanks in advance. John Caspers
jik@athena.mit.edu (Jonathan I. Kamens) (02/27/91)
In article <1991Feb26.101626.21779@fwi.uva.nl>, caspers@fwi.uva.nl (B.M. Caspers (I)) writes: |> I need to write a function that calls the win_printf of the DESQview API C |> library, which in its turn (I think) calls printf. Since I don't want to do |> any argument processing that printf should do, can I give the format string and the rest of the arguments unchanged to [win_]printf? Is there a win_vprintf? If so, that's what you're going to have to use. If not, you're in trouble. See the section entitled "Variable-Length Argument Lists" in the monthly Frequently Asked Questions posting to this newsgroup. If it isn't around at your site, E-mail me and I'll send it to you. -- Jonathan Kamens USnail: MIT Project Athena 11 Ashford Terrace jik@Athena.MIT.EDU Allston, MA 02134 Office: 617-253-8085 Home: 617-782-0710
brianh@hpcvia.CV.HP.COM (brian_helterline) (02/28/91)
jik@athena.mit.edu (Jonathan I. Kamens) writes: >In article <1991Feb26.101626.21779@fwi.uva.nl>, caspers@fwi.uva.nl (B.M. Caspers (I)) writes: >|> I need to write a function that calls the win_printf of the DESQview API C >|> library, which in its turn (I think) calls printf. Since I don't want to do >|> any argument processing that printf should do, can I give the format string and the rest of the arguments unchanged to [win_]printf? > > Is there a win_vprintf? If so, that's what you're going to have to use. If >not, you're in trouble. Not necesarily, you're only in trouble if you don't have vsprintf() since you can use that to create a single char array argument and pass that to win_printf. Of course, figuring out how large of a char array is needed is not always obvious. > > See the section entitled "Variable-Length Argument Lists" in the monthly >Frequently Asked Questions posting to this newsgroup. If it isn't around at >your site, E-mail me and I'll send it to you.
scs@adam.mit.edu (Steve Summit) (02/28/91)
In article <31530036@hpcvia.CV.HP.COM> brianh@hpcvia.CV.HP.COM (brian_helterline) writes: >jik@athena.mit.edu (Jonathan I. Kamens) writes: >> Is there a win_vprintf? If so, that's what you're going to have to use. >> If not, you're in trouble. >Not necesarily, you're only in trouble if you don't have vsprintf() since >you can use that to create a single char array argument and pass that to >win_printf. Of course, figuring out how large of a char array is needed >is not always obvious. It is not always *possible*. In fact, it is usually impossible, which is why any solution involving vsprintf is decidedly inferior. However, if you don't have something you can hand a va_list to (what Jon suggested as win_vprintf), vsprintf is all you're left with. Here's how the vsprintf/win_printf example might "work:" my_printf(char *fmt, ...) { va_list argp; char tmpbuf[80]; /* XXX */ va_start(argp, fmt); vsprintf(tmpbuf, fmt, argp); va_end(argp); win_printf("%s", tmpbuf); } If you think this is acceptable code, let me tell you about the Internet worm. There is NO WAY to declare/allocate that temporary buffer "correctly." If we are writing win_printf as a black box, with no knowledge of its likely calling patterns, we can't put any kind of upper bound on how much output one call to it might generate. When I have to allocate temporary buffers under these circumstances, I usually do so dynamically and very conservatively: char *tmpbuf = malloc(strlen(fmt) * 3 + 100); but this is still vulnerable, because the fmt might contain %s format specifiers which pull in arbitrary-length strings. The problem is worse because we can't even arrange for the code to fail gracefully if the temporary buffer overflows. About the best we could do is if(vsprintf(tmpbuf, fmt, argp) + 1 > 80) /* or whatever */ _exit(1); because by the time we can tell that the length of the formatted result exceeds the buffer size, it's too late: the buffer has already been overfilled, and we don't know what else in memory has been wiped out. In light of the above difficulties, I would offer a plea to library writers everywhere: if you are providing any printf-like routines, *always* provide v*printf counterparts. If you are implementing your printf lookalikes correctly, it is impossible for it to be difficult to provide companion versions which accept a va_list. (I'm not going to prove this assertion. After thinking about va_list-using code in general, and printf/vprintf in particular, and after reading the FAQ list if you have to, if you still think that writing win_vprintf might be any harder than writing win_printf, send me mail.) Until third-party library vendors everywhere start providing vprintf-like functions regularly, there are a couple of routines in the standard library which could help us, except that they are not actually in the standard library (yet). First, note that sprintf is just as bad as gets (to re-evoke Internet worm paranoia). There is NO WAY to prevent buffer overflow when using sprintf (and, equivalently, vsprintf), except of course if you have complete control over the format string and optional arguments. A simple solution, which ought to be more widespread by now, is to provide snprintf(char *buf, int buflen, char *fmt, ...) and vsnprintf(char *buf, int buflen, char *fmt, va_list argp) which are just like sprintf and vsprintf except that they LET YOU SPECIFY THE BUFFER SIZE so that it can't be overflowed (what a concept). If you know how sprintf is implemented, you know that implementing snprintf is trivial. (You have to watch out that you never try to fflush the string buffer, which becomes somewhat more likely when the buffer size isn't 32767 /* XXX */, but if that isn't a good use for the undocumented, internal _IOSTRG flag, which many stdio's carefully define and set for sprintf FILEs but then never check, I don't know what is.) I believe that gcc comes with a snprintf, and it's also in Chris Torek's recent 4bsd stdio implementation. Get the word out! These improvements will never be standardized until there is prior art. For an even better solution, Wouldn't It Be Loverly if there were a char *saprintf(char *fmt, ...) and a companion char *vsaprintf(char *fmt, va_list argp) which return a pointer to malloc'ed memory just large enough for, and containing, the resulting, formatted string? If you had something like vsaprintf available, you could write my_printf in terms of win_printf without fear of buffer overflow and without truncating any messages (as a vsnprintf solution might do). I have to admit that the names saprintf and vsaprintf are not ideal (the "a" is supposed to stand for "allocated"), and that there is not much prior art. (The stdio package I posted to alt.sources a year or so ago, which implemented them, seems not to have seen much use.) Of course, while I'm fantasizing about standard library extensions, there's another one which solves the underlying problem here even more cleanly. Since we would need new, nonstandard mechanisms (vsnprintf or vsaprintf) before we could write a proper, black-box implementation of my_printf in terms of win_printf, we might as well shoot for a granddaddy new, nonstandard mechanism which would let us write a routine like FILE *win_fopen(window_descriptor wd); which returns a FILE * that we can perform *any* stdio (output) operation on. my_printf is now unnecessary; plain old fprintf (on the FILE * returned by win_fopen) suffices. Most of the people who have stayed with me this far know what I'm leading up to: the so-called "function" stdio packages (Chris Torek talks about 'em all the time, and of course his recent implementation is one, as was mine). Once you can rig it up so that an I/O stream is implemented not necessarily in terms of low-level read and write, but rather in terms of any function you want, you can write win_fopen with ease: it just just returns a FILE * which is arranged to perform fflushes using win_printf (or, more likely, win_write). Furthermore, you can also write FILE *stropen(char *buf) and FILE *strnopen(char *buf, int buflen) and the pair FILE *straopen() char *straclose(FILE *fp) which return FILE *'s "open" on, respectively, a string, a string with length checking, and a dynamically-allocated string which grows while you do output to it and returns the final (malloc'ed) pointer when closed. With these in hand, you can write sprintf, snprintf, and saprintf all in terms of a simple vfprintf, and of course you can also perform any long, involved sequence of stdio operations while building up (or reading) a string. Steve Summit scs@adam.mit.edu
drh@duke.cs.duke.edu (D. Richard Hipp) (02/28/91)
In article <1991Feb27.234041.12579@athena.mit.edu> scs@adam.mit.edu writes: >A simple solution, which ought to be more widespread by now, is >to provide > snprintf(char *buf, int buflen, char *fmt, ...) >and > vsnprintf(char *buf, int buflen, char *fmt, va_list argp) >which are just like sprintf and vsprintf except that they LET YOU >SPECIFY THE BUFFER SIZE so that it can't be overflowed (what a >concept). [Stuff omitted] >For an even better solution, Wouldn't It Be Loverly if there were a > char *saprintf(char *fmt, ...) >and a companion > char *vsaprintf(char *fmt, va_list argp) >which return a pointer to malloc'ed memory just large enough for, >and containing, the resulting, formatted string? I have source code for the four functions described above, plus several other variations on the printf theme. In particular, I have a package that implements the following: snprintf Print to a string of no more than N characters. mprintf Get memory from malloc to hold a string. xprintf Call a function (possibly several times) to dispose of the output. nprintf Generate no output, but return the number of characters that would have been output if the regular printf had been called instead. Source code to these is available upon request to drh@cs.duke.edu.
mlw@taffy.bellcore.com (Mary Lu Wason) (03/01/91)
You can try this: my_printf(debug,format,a,b,c,d,e,f,g,h,i,j,k) int debug; char *format; int a,b,c,d,e,f,g,h,i,j,k; { if(debug){ fprintf(stderr,"MYDBG:"); fprintf(stderr,format,a,b,c,d,e,f,g,h,i,j,k); } } This works on both Unisys and MVS. Yes, a - k can be longs and chars etc. printf uses the format string to figure what they are. printf doesn't look for any more arguments than are in the format string. Of course, for this you have to take a guess as to how many arguments will be used. -Mary Lu
henry@zoo.toronto.edu (Henry Spencer) (03/01/91)
In article <1991Feb28.110727@taffy.bellcore.com> mlw@elvislu.soac.bellcore.com writes: > my_printf(debug,format,a,b,c,d,e,f,g,h,i,j,k) > Of course, for this you have to take a guess as to > how many arguments will be used. There are an increasing number of machines on which this simply won't work, regardless of how accurate your guesses are. -- "But this *is* the simplified version | Henry Spencer @ U of Toronto Zoology for the general public." -S. Harris | henry@zoo.toronto.edu utzoo!henry