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