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