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