1 /*
2 * Replacement for a missing snprintf or vsnprintf.
3 *
4 * The following implementation of snprintf was taken mostly verbatim from
5 * <http://www.fiction.net/blong/programs/>; it is the version of snprintf
6 * used in Mutt. A possibly newer version is used in wget, found at
7 * <https://github.com/wertarbyte/wget/blob/master/src/snprintf.c>.
8 *
9 * Please do not reformat or otherwise change this file more than necessary so
10 * that later merges with the original source are easy. Bug fixes and
11 * improvements should be sent back to the original author.
12 *
13 * The canonical version of this file is maintained in the rra-c-util package,
14 * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
15 */
16
17 /*
18 * If we're running the test suite, rename snprintf and vsnprintf to avoid
19 * conflicts with the system version.
20 */
21 #if TESTING
22 # define snprintf test_snprintf
23 # define vsnprintf test_vsnprintf
24 #endif
25
26 /*
27 * Copyright Patrick Powell 1995
28 * This code is based on code written by Patrick Powell (papowell@astart.com)
29 * It may be used for any purpose as long as this notice remains intact
30 * on all source code distributions
31 */
32
33 /**************************************************************
34 * Original:
35 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
36 * A bombproof version of doprnt (dopr) included.
37 * Sigh. This sort of thing is always nasty do deal with. Note that
38 * the version here does not include floating point...
39 *
40 * snprintf() is used instead of sprintf() as it does limit checks
41 * for string length. This covers a nasty loophole.
42 *
43 * The other functions are there to prevent NULL pointers from
44 * causing nast effects.
45 *
46 * More Recently:
47 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
48 * This was ugly. It is still ugly. I opted out of floating point
49 * numbers, but the formatter understands just about everything
50 * from the normal C string format, at least as far as I can tell from
51 * the Solaris 2.5 printf(3S) man page.
52 *
53 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
54 * Ok, added some minimal floating point support, which means this
55 * probably requires libm on most operating systems. Don't yet
56 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
57 * was pretty badly broken, it just wasn't being exercised in ways
58 * which showed it, so that's been fixed. Also, formated the code
59 * to mutt conventions, and removed dead code left over from the
60 * original. Also, there is now a builtin-test, just compile with:
61 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
62 * and run snprintf for results.
63 *
64 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
65 * The PGP code was using unsigned hexadecimal formats.
66 * Unfortunately, unsigned formats simply didn't work.
67 *
68 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
69 * The original code assumed that both snprintf() and vsnprintf() were
70 * missing. Some systems only have snprintf() but not vsnprintf(), so
71 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
72 *
73 * Andrew Tridgell (tridge@samba.org) Oct 1998
74 * fixed handling of %.0f
75 * added test for HAVE_LONG_DOUBLE
76 *
77 * Russ Allbery <eagle@eyrie.org> 2000-08-26
78 * fixed return value to comply with C99
79 * fixed handling of snprintf(NULL, ...)
80 *
81 * Hrvoje Niksic <hniksic@arsdigita.com> 2000-11-04
82 * include <stdio.h> for NULL.
83 * added support for long long.
84 * don't declare argument types to (v)snprintf if stdarg is not used.
85 *
86 * Hrvoje Niksic <hniksic@xemacs.org> 2005-04-15
87 * use the PARAMS macro to handle prototypes.
88 * write function definitions in the ansi2knr-friendly way.
89 * if string precision is specified, don't read VALUE past it.
90 * fix bug in fmtfp that caused 0.01 to be printed as 0.1.
91 * don't include <ctype.h> because none of it is used.
92 * interpret precision as number of significant digits with %g
93 * omit trailing decimal zeros with %g
94 *
95 **************************************************************/
96
97 #include <config.h>
98
99 #include <string.h>
100 #include <ctype.h>
101 #include <sys/types.h>
102
103 #ifndef NULL
104 # define NULL 0
105 #endif
106
107 /* varargs declarations: */
108
109 #include <stdarg.h>
110 #define HAVE_STDARGS /* let's hope that works everywhere (mj) */
111 #define VA_LOCAL_DECL va_list ap
112 #define VA_START(f) va_start(ap, f)
113 #define VA_SHIFT(v,t) ; /* no-op for ANSI */
114 #define VA_END va_end(ap)
115
116 /* Assume all compilers support long double, per Autoconf documentation. */
117 #define LDOUBLE long double
118
119 #ifdef HAVE_LONG_LONG_INT
120 # define LLONG long long
121 #else
122 # define LLONG long
123 #endif
124
125 int snprintf (char *str, size_t count, const char *fmt, ...);
126 int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
127
128 static int dopr (char *buffer, size_t maxlen, const char *format,
129 va_list args);
130 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
131 const char *value, int flags, int min, int max);
132 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
133 LLONG value, int base, int min, int max, int flags);
134 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
135 LDOUBLE fvalue, int min, int max, int flags);
136 static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
137
138 /*
139 * dopr(): poor man's version of doprintf
140 */
141
142 /* format read states */
143 #define DP_S_DEFAULT 0
144 #define DP_S_FLAGS 1
145 #define DP_S_MIN 2
146 #define DP_S_DOT 3
147 #define DP_S_MAX 4
148 #define DP_S_MOD 5
149 #define DP_S_MOD_L 6
150 #define DP_S_CONV 7
151 #define DP_S_DONE 8
152
153 /* format flags - Bits */
154 #define DP_F_MINUS (1 << 0)
155 #define DP_F_PLUS (1 << 1)
156 #define DP_F_SPACE (1 << 2)
157 #define DP_F_NUM (1 << 3)
158 #define DP_F_ZERO (1 << 4)
159 #define DP_F_UP (1 << 5)
160 #define DP_F_UNSIGNED (1 << 6)
161 #define DP_F_FP_G (1 << 7)
162
163 /* Conversion Flags */
164 #define DP_C_SHORT 1
165 #define DP_C_LONG 2
166 #define DP_C_LLONG 3
167 #define DP_C_LDOUBLE 4
168
169 #define char_to_int(p) (p - '0')
170 #define MAX(p,q) ((p >= q) ? p : q)
171 #define MIN(p,q) ((p <= q) ? p : q)
172
dopr(char * buffer,size_t maxlen,const char * format,va_list args)173 static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
174 {
175 char ch;
176 LLONG value;
177 LDOUBLE fvalue;
178 char *strvalue;
179 int min;
180 int max;
181 int state;
182 int flags;
183 int cflags;
184 int total;
185 size_t currlen;
186
187 state = DP_S_DEFAULT;
188 currlen = flags = cflags = min = 0;
189 max = -1;
190 ch = *format++;
191 total = 0;
192
193 while (state != DP_S_DONE)
194 {
195 if (ch == '\0')
196 state = DP_S_DONE;
197
198 switch(state)
199 {
200 case DP_S_DEFAULT:
201 if (ch == '%')
202 state = DP_S_FLAGS;
203 else
204 total += dopr_outch (buffer, &currlen, maxlen, ch);
205 ch = *format++;
206 break;
207 case DP_S_FLAGS:
208 switch (ch)
209 {
210 case '-':
211 flags |= DP_F_MINUS;
212 ch = *format++;
213 break;
214 case '+':
215 flags |= DP_F_PLUS;
216 ch = *format++;
217 break;
218 case ' ':
219 flags |= DP_F_SPACE;
220 ch = *format++;
221 break;
222 case '#':
223 flags |= DP_F_NUM;
224 ch = *format++;
225 break;
226 case '0':
227 flags |= DP_F_ZERO;
228 ch = *format++;
229 break;
230 default:
231 state = DP_S_MIN;
232 break;
233 }
234 break;
235 case DP_S_MIN:
236 if ('0' <= ch && ch <= '9')
237 {
238 min = 10*min + char_to_int (ch);
239 ch = *format++;
240 }
241 else if (ch == '*')
242 {
243 min = va_arg (args, int);
244 ch = *format++;
245 state = DP_S_DOT;
246 }
247 else
248 state = DP_S_DOT;
249 break;
250 case DP_S_DOT:
251 if (ch == '.')
252 {
253 state = DP_S_MAX;
254 ch = *format++;
255 }
256 else
257 state = DP_S_MOD;
258 break;
259 case DP_S_MAX:
260 if ('0' <= ch && ch <= '9')
261 {
262 if (max < 0)
263 max = 0;
264 max = 10*max + char_to_int (ch);
265 ch = *format++;
266 }
267 else if (ch == '*')
268 {
269 max = va_arg (args, int);
270 ch = *format++;
271 state = DP_S_MOD;
272 }
273 else
274 state = DP_S_MOD;
275 break;
276 case DP_S_MOD:
277 switch (ch)
278 {
279 case 'h':
280 cflags = DP_C_SHORT;
281 ch = *format++;
282 break;
283 case 'l':
284 cflags = DP_C_LONG;
285 ch = *format++;
286 break;
287 case 'L':
288 cflags = DP_C_LDOUBLE;
289 ch = *format++;
290 break;
291 default:
292 break;
293 }
294 if (cflags != DP_C_LONG)
295 state = DP_S_CONV;
296 else
297 state = DP_S_MOD_L;
298 break;
299 case DP_S_MOD_L:
300 switch (ch)
301 {
302 case 'l':
303 cflags = DP_C_LLONG;
304 ch = *format++;
305 break;
306 default:
307 break;
308 }
309 state = DP_S_CONV;
310 break;
311 case DP_S_CONV:
312 switch (ch)
313 {
314 case 'd':
315 case 'i':
316 if (cflags == DP_C_SHORT)
317 value = (short int) va_arg (args, int);
318 else if (cflags == DP_C_LONG)
319 value = va_arg (args, long int);
320 else if (cflags == DP_C_LLONG)
321 value = va_arg (args, LLONG);
322 else
323 value = va_arg (args, int);
324 total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
325 break;
326 case 'o':
327 flags |= DP_F_UNSIGNED;
328 if (cflags == DP_C_SHORT)
329 value = (unsigned short int) va_arg (args, unsigned int);
330 else if (cflags == DP_C_LONG)
331 value = va_arg (args, unsigned long int);
332 else if (cflags == DP_C_LLONG)
333 value = va_arg (args, unsigned LLONG);
334 else
335 value = va_arg (args, unsigned int);
336 total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
337 break;
338 case 'u':
339 flags |= DP_F_UNSIGNED;
340 if (cflags == DP_C_SHORT)
341 value = (unsigned short int) va_arg (args, unsigned int);
342 else if (cflags == DP_C_LONG)
343 value = va_arg (args, unsigned long int);
344 else if (cflags == DP_C_LLONG)
345 value = va_arg (args, unsigned LLONG);
346 else
347 value = va_arg (args, unsigned int);
348 total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
349 break;
350 case 'X':
351 flags |= DP_F_UP;
352 case 'x':
353 flags |= DP_F_UNSIGNED;
354 if (cflags == DP_C_SHORT)
355 value = (unsigned short int) va_arg (args, unsigned int);
356 else if (cflags == DP_C_LONG)
357 value = va_arg (args, unsigned long int);
358 else if (cflags == DP_C_LLONG)
359 value = va_arg (args, unsigned LLONG);
360 else
361 value = va_arg (args, unsigned int);
362 total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
363 break;
364 case 'f':
365 if (cflags == DP_C_LDOUBLE)
366 fvalue = va_arg (args, LDOUBLE);
367 else
368 fvalue = va_arg (args, double);
369 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
370 break;
371 case 'E':
372 flags |= DP_F_UP;
373 case 'e':
374 if (cflags == DP_C_LDOUBLE)
375 fvalue = va_arg (args, LDOUBLE);
376 else
377 fvalue = va_arg (args, double);
378 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
379 break;
380 case 'G':
381 flags |= DP_F_UP;
382 case 'g':
383 flags |= DP_F_FP_G;
384 if (cflags == DP_C_LDOUBLE)
385 fvalue = va_arg (args, LDOUBLE);
386 else
387 fvalue = va_arg (args, double);
388 if (max == 0)
389 /* C99 says: if precision [for %g] is zero, it is taken as one */
390 max = 1;
391 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
392 break;
393 case 'c':
394 total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
395 break;
396 case 's':
397 strvalue = va_arg (args, char *);
398 total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
399 break;
400 case 'p':
401 strvalue = va_arg (args, void *);
402 total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
403 max, flags);
404 break;
405 case 'n':
406 if (cflags == DP_C_SHORT)
407 {
408 short int *num;
409 num = va_arg (args, short int *);
410 *num = currlen;
411 }
412 else if (cflags == DP_C_LONG)
413 {
414 long int *num;
415 num = va_arg (args, long int *);
416 *num = currlen;
417 }
418 else if (cflags == DP_C_LLONG)
419 {
420 LLONG *num;
421 num = va_arg (args, LLONG *);
422 *num = currlen;
423 }
424 else
425 {
426 int *num;
427 num = va_arg (args, int *);
428 *num = currlen;
429 }
430 break;
431 case '%':
432 total += dopr_outch (buffer, &currlen, maxlen, ch);
433 break;
434 case 'w':
435 /* not supported yet, treat as next char */
436 format++;
437 break;
438 default:
439 /* Unknown, skip */
440 break;
441 }
442 ch = *format++;
443 state = DP_S_DEFAULT;
444 flags = cflags = min = 0;
445 max = -1;
446 break;
447 case DP_S_DONE:
448 break;
449 default:
450 /* hmm? */
451 break; /* some picky compilers need this */
452 }
453 }
454 if (buffer != NULL)
455 {
456 if (currlen < maxlen - 1)
457 buffer[currlen] = '\0';
458 else
459 buffer[maxlen - 1] = '\0';
460 }
461 return total;
462 }
463
fmtstr(char * buffer,size_t * currlen,size_t maxlen,const char * value,int flags,int min,int max)464 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
465 const char *value, int flags, int min, int max)
466 {
467 int padlen, strln; /* amount to pad */
468 int cnt = 0;
469 int total = 0;
470
471 if (value == 0)
472 {
473 value = "(null)";
474 }
475
476 if (max < 0)
477 strln = strlen (value);
478 else
479 /* When precision is specified, don't read VALUE past precision. */
480 /*strln = strnlen (value, max);*/
481 for (strln = 0; strln < max && value[strln]; ++strln);
482 padlen = min - strln;
483 if (padlen < 0)
484 padlen = 0;
485 if (flags & DP_F_MINUS)
486 padlen = -padlen; /* Left Justify */
487
488 while (padlen > 0)
489 {
490 total += dopr_outch (buffer, currlen, maxlen, ' ');
491 --padlen;
492 }
493 while (*value && ((max < 0) || (cnt < max)))
494 {
495 total += dopr_outch (buffer, currlen, maxlen, *value++);
496 ++cnt;
497 }
498 while (padlen < 0)
499 {
500 total += dopr_outch (buffer, currlen, maxlen, ' ');
501 ++padlen;
502 }
503 return total;
504 }
505
506 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
507
fmtint(char * buffer,size_t * currlen,size_t maxlen,LLONG value,int base,int min,int max,int flags)508 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
509 LLONG value, int base, int min, int max, int flags)
510 {
511 int signvalue = 0;
512 unsigned LLONG uvalue;
513 char convert[24];
514 unsigned int place = 0;
515 int spadlen = 0; /* amount to space pad */
516 int zpadlen = 0; /* amount to zero pad */
517 const char *digits;
518 int total = 0;
519
520 if (max < 0)
521 max = 0;
522
523 uvalue = value;
524
525 if(!(flags & DP_F_UNSIGNED))
526 {
527 if( value < 0 ) {
528 signvalue = '-';
529 uvalue = -value;
530 }
531 else
532 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
533 signvalue = '+';
534 else
535 if (flags & DP_F_SPACE)
536 signvalue = ' ';
537 }
538
539 if (flags & DP_F_UP)
540 /* Should characters be upper case? */
541 digits = "0123456789ABCDEF";
542 else
543 digits = "0123456789abcdef";
544
545 do {
546 convert[place++] = digits[uvalue % (unsigned)base];
547 uvalue = (uvalue / (unsigned)base );
548 } while(uvalue && (place < sizeof (convert)));
549 if (place == sizeof (convert)) place--;
550 convert[place] = 0;
551
552 zpadlen = max - place;
553 spadlen = min - MAX ((unsigned int)max, place) - (signvalue ? 1 : 0);
554 if (zpadlen < 0) zpadlen = 0;
555 if (spadlen < 0) spadlen = 0;
556 if (flags & DP_F_ZERO)
557 {
558 zpadlen = MAX(zpadlen, spadlen);
559 spadlen = 0;
560 }
561 if (flags & DP_F_MINUS)
562 spadlen = -spadlen; /* Left Justifty */
563
564 #ifdef DEBUG_SNPRINTF
565 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
566 zpadlen, spadlen, min, max, place));
567 #endif
568
569 /* Spaces */
570 while (spadlen > 0)
571 {
572 total += dopr_outch (buffer, currlen, maxlen, ' ');
573 --spadlen;
574 }
575
576 /* Sign */
577 if (signvalue)
578 total += dopr_outch (buffer, currlen, maxlen, signvalue);
579
580 /* Zeros */
581 if (zpadlen > 0)
582 {
583 while (zpadlen > 0)
584 {
585 total += dopr_outch (buffer, currlen, maxlen, '0');
586 --zpadlen;
587 }
588 }
589
590 /* Digits */
591 while (place > 0)
592 total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
593
594 /* Left Justified spaces */
595 while (spadlen < 0) {
596 total += dopr_outch (buffer, currlen, maxlen, ' ');
597 ++spadlen;
598 }
599
600 return total;
601 }
602
abs_val(LDOUBLE value)603 static LDOUBLE abs_val (LDOUBLE value)
604 {
605 LDOUBLE result = value;
606
607 if (value < 0)
608 result = -value;
609
610 return result;
611 }
612
pow10_int(int exp)613 static LDOUBLE pow10_int (int exp)
614 {
615 LDOUBLE result = 1;
616
617 while (exp)
618 {
619 result *= 10;
620 exp--;
621 }
622
623 return result;
624 }
625
round_int(LDOUBLE value)626 static LLONG round_int (LDOUBLE value)
627 {
628 LLONG intpart;
629
630 intpart = value;
631 value = value - intpart;
632 if (value >= 0.5)
633 intpart++;
634
635 return intpart;
636 }
637
fmtfp(char * buffer,size_t * currlen,size_t maxlen,LDOUBLE fvalue,int min,int max,int flags)638 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
639 LDOUBLE fvalue, int min, int max, int flags)
640 {
641 int signvalue = 0;
642 LDOUBLE ufvalue;
643 char iconvert[24];
644 char fconvert[24];
645 size_t iplace = 0;
646 size_t fplace = 0;
647 int padlen = 0; /* amount to pad */
648 int zpadlen = 0;
649 int total = 0;
650 LLONG intpart;
651 LLONG fracpart;
652 LLONG mask10;
653 int leadingfrac0s = 0; /* zeroes at the start of fractional part */
654 int omitzeros = 0;
655 size_t omitcount = 0;
656
657 /*
658 * AIX manpage says the default is 0, but Solaris says the default
659 * is 6, and sprintf on AIX defaults to 6
660 */
661 if (max < 0)
662 max = 6;
663
664 ufvalue = abs_val (fvalue);
665
666 if (fvalue < 0)
667 signvalue = '-';
668 else
669 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
670 signvalue = '+';
671 else
672 if (flags & DP_F_SPACE)
673 signvalue = ' ';
674
675 #if 0
676 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
677 #endif
678
679 intpart = ufvalue;
680
681 /* With %g precision is the number of significant digits, which
682 includes the digits in intpart. */
683 if (flags & DP_F_FP_G)
684 {
685 if (intpart != 0)
686 {
687 /* For each digit of INTPART, print one less fractional digit. */
688 LLONG temp = intpart;
689 for (temp = intpart; temp != 0; temp /= 10)
690 --max;
691 if (max < 0)
692 max = 0;
693 }
694 else
695 {
696 /* For each leading 0 in fractional part, print one more
697 fractional digit. */
698 LDOUBLE temp;
699 if (ufvalue > 0)
700 for (temp = ufvalue; temp < 0.1; temp *= 10)
701 ++max;
702 }
703 }
704
705 /* C99: trailing zeros are removed from the fractional portion of the
706 result unless the # flag is specified */
707 if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM))
708 omitzeros = 1;
709
710 #if SIZEOF_LONG_LONG > 0
711 # define MAX_DIGITS 18 /* grok more digits with long long */
712 #else
713 # define MAX_DIGITS 9 /* just long */
714 #endif
715
716 /*
717 * Sorry, we only support several digits past the decimal because of
718 * our conversion method
719 */
720 if (max > MAX_DIGITS)
721 max = MAX_DIGITS;
722
723 /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */
724 mask10 = pow10_int (max);
725
726 /* We "cheat" by converting the fractional part to integer by
727 * multiplying by a factor of 10
728 */
729 fracpart = round_int (mask10 * (ufvalue - intpart));
730
731 if (fracpart >= mask10)
732 {
733 intpart++;
734 fracpart -= mask10;
735 }
736 else if (fracpart != 0)
737 /* If fracpart has less digits than the 10* mask, we need to
738 manually insert leading 0s. For example 2.01's fractional part
739 requires one leading zero to distinguish it from 2.1. */
740 while (fracpart < mask10 / 10)
741 {
742 ++leadingfrac0s;
743 mask10 /= 10;
744 }
745
746 #ifdef DEBUG_SNPRINTF
747 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
748 #endif
749
750 /* Convert integer part */
751 do {
752 iconvert[iplace++] = '0' + intpart % 10;
753 intpart = (intpart / 10);
754 } while(intpart && (iplace < sizeof(iconvert)));
755 if (iplace == sizeof(iconvert)) iplace--;
756 iconvert[iplace] = 0;
757
758 /* Convert fractional part */
759 do {
760 fconvert[fplace++] = '0' + fracpart % 10;
761 fracpart = (fracpart / 10);
762 } while(fracpart && (fplace < sizeof(fconvert)));
763 while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert))
764 fconvert[fplace++] = '0';
765 if (fplace == sizeof(fconvert)) fplace--;
766 fconvert[fplace] = 0;
767 if (omitzeros)
768 while (omitcount < fplace && fconvert[omitcount] == '0')
769 ++omitcount;
770
771 /* -1 for decimal point, another -1 if we are printing a sign */
772 padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0);
773 if (!omitzeros)
774 zpadlen = max - fplace;
775 if (zpadlen < 0)
776 zpadlen = 0;
777 if (padlen < 0)
778 padlen = 0;
779 if (flags & DP_F_MINUS)
780 padlen = -padlen; /* Left Justifty */
781
782 if ((flags & DP_F_ZERO) && (padlen > 0))
783 {
784 if (signvalue)
785 {
786 total += dopr_outch (buffer, currlen, maxlen, signvalue);
787 --padlen;
788 signvalue = 0;
789 }
790 while (padlen > 0)
791 {
792 total += dopr_outch (buffer, currlen, maxlen, '0');
793 --padlen;
794 }
795 }
796 while (padlen > 0)
797 {
798 total += dopr_outch (buffer, currlen, maxlen, ' ');
799 --padlen;
800 }
801 if (signvalue)
802 total += dopr_outch (buffer, currlen, maxlen, signvalue);
803
804 while (iplace > 0)
805 total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
806
807 /*
808 * Decimal point. This should probably use locale to find the correct
809 * char to print out.
810 */
811 if (max > 0 && (fplace > omitcount || zpadlen > 0))
812 {
813 total += dopr_outch (buffer, currlen, maxlen, '.');
814
815 while (fplace > omitcount)
816 total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
817 }
818
819 while (zpadlen > 0)
820 {
821 total += dopr_outch (buffer, currlen, maxlen, '0');
822 --zpadlen;
823 }
824
825 while (padlen < 0)
826 {
827 total += dopr_outch (buffer, currlen, maxlen, ' ');
828 ++padlen;
829 }
830
831 return total;
832 }
833
dopr_outch(char * buffer,size_t * currlen,size_t maxlen,char c)834 static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
835 {
836 if (*currlen + 1 < maxlen)
837 buffer[(*currlen)++] = c;
838 return 1;
839 }
840
vsnprintf(char * str,size_t count,const char * fmt,va_list args)841 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
842 {
843 if (str != NULL)
844 str[0] = 0;
845 return dopr(str, count, fmt, args);
846 }
847
848 /* VARARGS3 */
849 #ifdef HAVE_STDARGS
snprintf(char * str,size_t count,const char * fmt,...)850 int snprintf (char *str,size_t count,const char *fmt,...)
851 #else
852 int snprintf (va_alist) va_dcl
853 #endif
854 {
855 #ifndef HAVE_STDARGS
856 char *str;
857 size_t count;
858 char *fmt;
859 #endif
860 VA_LOCAL_DECL;
861 int total;
862
863 VA_START (fmt);
864 VA_SHIFT (str, char *);
865 VA_SHIFT (count, size_t );
866 VA_SHIFT (fmt, char *);
867 total = vsnprintf(str, count, fmt, ap);
868 VA_END;
869 return total;
870 }
871
872 #ifdef TEST_SNPRINTF
873 #ifndef LONG_STRING
874 #define LONG_STRING 1024
875 #endif
main(void)876 int main (void)
877 {
878 char buf1[LONG_STRING];
879 char buf2[LONG_STRING];
880 char *fp_fmt[] = {
881 "%-1.5f",
882 "%1.5f",
883 "%123.9f",
884 "%10.5f",
885 "% 10.5f",
886 "%+22.9f",
887 "%+4.9f",
888 "%01.3f",
889 "%4f",
890 "%3.1f",
891 "%3.2f",
892 "%.0f",
893 "%.1f",
894 NULL
895 };
896 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
897 0.9996, 1.996, 4.136, 0};
898 char *int_fmt[] = {
899 "%-1.5d",
900 "%1.5d",
901 "%123.9d",
902 "%5.5d",
903 "%10.5d",
904 "% 10.5d",
905 "%+22.33d",
906 "%01.3d",
907 "%4d",
908 NULL
909 };
910 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
911 int x, y;
912 int fail = 0;
913 int num = 0;
914
915 printf ("Testing snprintf format codes against system sprintf...\n");
916
917 for (x = 0; fp_fmt[x] != NULL ; x++)
918 for (y = 0; fp_nums[y] != 0 ; y++)
919 {
920 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
921 sprintf (buf2, fp_fmt[x], fp_nums[y]);
922 if (strcmp (buf1, buf2))
923 {
924 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
925 fp_fmt[x], buf1, buf2);
926 fail++;
927 }
928 num++;
929 }
930
931 for (x = 0; int_fmt[x] != NULL ; x++)
932 for (y = 0; int_nums[y] != 0 ; y++)
933 {
934 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
935 sprintf (buf2, int_fmt[x], int_nums[y]);
936 if (strcmp (buf1, buf2))
937 {
938 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
939 int_fmt[x], buf1, buf2);
940 fail++;
941 }
942 num++;
943 }
944 printf ("%d tests failed out of %d.\n", fail, num);
945 }
946 #endif /* SNPRINTF_TEST */
947