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