vook@narnia.rtp.dg.com (Eric R Vook) (06/06/91)
Harbison & Steele p.328 contains the following parenthetical comment after a discussion of sprintf(): "(It is the programmer's responsibility to ensure that the destination string area is large enough to contain the output generated by the formatting operation.)" ["C: A Reference Manual," 2nd ed, Harbison & Steele, p.328] I have just a short question, two-part question: How do you know how much space to allocate? Or How do you know when you didn't allocate enough? My application has a varargs function which needs to eventually get to a single string to pass to routines which do NOT take a varargs interface. -Eric --------------------------------------------------------------------- Eric R Vook | Face reality as it is, Data General Corporation | not as it was vook@dg-rtp.dg.com | or as you wish it were. (Jack Welch)
torek@elf.ee.lbl.gov (Chris Torek) (06/06/91)
In article <1991Jun5.174543.266@dg-rtp.dg.com> vook@narnia.rtp.dg.com (Eric R Vook) writes: >How do you know how much space to allocate [for sprintf]? >How do you know when you didn't allocate enough? A very good question indeed---by which I mean there are no good answers. The new BSD stdio has snprintf and vsnprintf functions, which take a buffer length, and guarantee not to overrun that buffer; they both return the number of characters they would have printed, were the buffer infinitely long, and thus: char small[40], *p = small; needed = snprintf(small, sizeof small, "%s%s%s", a1, a2, a3); if (needed >= sizeof small) { /* the string was truncated */ p = malloc(needed + 1); if (p == NULL) die("malloc failed"); (void) snprintf(p, needed + 1, "%s%s%s", a1, a2, a3); } Since many uses of sprintf() are simply to transform the arguments into a set of characters so that those characters can be displayed somewhere (e.g., a window), the new BSD stdio also provides `funopen', through which you can open your own analogue to the `write' function. This allows you to pass arbitrarily long items through a short buffer. Of course, none of these are remotely portable. I have an approximation for a portable `vsnprintf', included below. It depends on vfprintf and on `byte' files (hence will not work with fancy record files such as found on VMS). It uses open-but-unlinked files (easily changed, provided you have atexit()). /* * The function we want is called `vsnprintf': sprintf, * with output limited to at most some number of bytes. * This exists in the `function stdio' library already. * Here we roll our own. * * MAJOR BOGOSITY: System V stdio has no way to limit the length * to an sprintf. Instead, we use vfprintf to a temp file which we * keep open but unlinked. */ #include <stdio.h> #include <varargs.h> static FILE * setfil() { register FILE *f; char buf[100]; (void) sprintf(buf, "/tmp/vsnprintf.%d", getpid()); if ((f = fopen(buf, "w+")) == NULL) { perror(buf); (void) fprintf(stderr, "cannot set up vsnprintf, help\n"); exit(1); } (void) unlink(buf); return (f); } int vsnprintf(str, len, fmt, ap) char *str; size_t len; char *fmt; va_list ap; { register int nch; static FILE *fil; if (len == 0) return (0); if (fil == NULL) fil = setfil(); (void) fseek(fil, 0L, 0); nch = vfprintf(fil, fmt, ap); (void) fseek(fil, 0L, 0); len--; if (nch < len) len = nch; nch = fread(str, 1, len, fil); str[nch] = '\0'; return (nch); } -- In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427) Berkeley, CA Domain: torek@ee.lbl.gov
henry@zoo.toronto.edu (Henry Spencer) (06/06/91)
In article <1991Jun5.174543.266@dg-rtp.dg.com> vook@narnia.rtp.dg.com (Eric R Vook) writes: >"(It is the programmer's responsibility to ensure that the destination > string area is large enough to contain the output generated by the > formatting operation.)" > > How do you know how much space to allocate? > How do you know when you didn't allocate enough? The fast answers are "you have to know how sprintf is being used and figure out the longest string your parameters can result in" and "basically, you don't". A length-limited sprintf was repeatedly proposed to X3J11, and might have made it into ANSI C if there had been some experience with it. -- "We're thinking about upgrading from | Henry Spencer @ U of Toronto Zoology SunOS 4.1.1 to SunOS 3.5." | henry@zoo.toronto.edu utzoo!henry
martin@mwtech.UUCP (Martin Weitzel) (06/07/91)
In article <1991Jun5.174543.266@dg-rtp.dg.com> vook@narnia.rtp.dg.com (Eric R Vook) writes: >"(It is the programmer's responsibility to ensure that the destination > string area is large enough to contain the output generated by the > formatting operation.)" > > How do you know how much space to allocate? > How do you know when you didn't allocate enough? In article <1991Jun6.162723.27307@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) answers: >The fast answers are "you have to know how sprintf is being used and figure >out the longest string your parameters can result in" and "basically, you >don't". This is also my standard answer and I generally avoid using sprintf if I can't reliably calculate the required size of the buffer. But since I know that there are some outthere who will continue to use sprintf also when the buffer size couldn't be reliably determined, I have a suggestion (which may also help to check your calculations if you *think* you made the buffer large enough). If you are too lazy to follow Chris Toreks complete solution to the problem, at least write your code as follows: ********* NOTE THAT THIS DOESN'T GUARANTEE TO CATCH ALL PROBLEMS ********* ********* ON ANY MACHINE WHEN WRITING PAST THE END OF THE BUFFER! ********* #define MAX 40 /* if you think that 40 is enough */ char buffer[MAX+1]; buffer[MAX] = '\0'; sprintf(buffer, ......); if (buffer[MAX] != '\0') abort(); ********* NOTE THAT THIS DOESN'T GUARANTEE TO CATCH ALL PROBLEMS ********* ********* ON ANY MACHINE WHEN WRITING PAST THE END OF THE BUFFER! ********* But in general it's better than nothing, as on many architectures you will first overwrite other local variables and possibly the stack frame, then parameters of the function which contains this code and then locals, stack frames, and parameters of the calling functions. If your function never returns nor uses any other locals you have a good chance to at least detect the problem immediately. ********* NOTE THAT THIS DOESN'T GUARANTEE TO CATCH ALL PROBLEMS ********* ********* ON ANY MACHINE WHEN WRITING PAST THE END OF THE BUFFER! ********* -- Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83
wirzeniu@klaava.Helsinki.FI (Lars Wirzenius) (06/09/91)
In article <1167@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes: >[ as a suggestion for handling problems when sprintf overflows the buffer ] > #define MAX 40 /* if you think that 40 is enough */ > char buffer[MAX+1]; > buffer[MAX] = '\0'; > sprintf(buffer, ......); > if (buffer[MAX] != '\0') abort(); May I further suggest that another character than '\0' be used. A frequent problem (for me, at least) is the 'off-by-one error' (e.g., some limit or array bound is one too small). This type of error would print one character too many to the buffer, but this isn't noticed if the validation character is '\0'. If available, a char value that doesn't correspond to a normal character would be best. -- Lars Wirzenius wirzeniu@cc.helsinki.fi
peic@core.north.de (Peter Eichler) (06/11/91)
In article <1991Jun5.174543.266@dg-rtp.dg.com>, Eric R Vook writes: > >Harbison & Steele p.328 contains the following parenthetical comment >after a discussion of sprintf(): >"(It is the programmer's responsibility to ensure that the destination > string area is large enough to contain the output generated by the > formatting operation.)" > ["C: A Reference Manual," 2nd ed, Harbison & Steele, p.328] > >I have just a short question, two-part question: > How do you know how much space to allocate? Well, look at the stuff you like to put into your destination string. If I'm printing 5000 floats, it should be more 100 Bytes... ;-) >Or > How do you know when you didn't allocate enough? If your machine crashes instantly, behaves strangely or your system crashes later, you WILL know (well maybe, case the error is often hard to find) > [...] >-Eric Cheerio, Peter Since pleasure is the Unique, to reveal Pleasure is itself a unique duty. FGTH ------------------------------------------------------------------------------ SNAIL:Peter Eichler \ Hegelstrasse 3 \ 2800 Bremen 1 \ Germany Amiga 3000 EMAIL:peic@core.north.de OR peic@skuld.north.de: VOICE:(+)49 421 530642