dd41ca330608ed92ef24fbdf6bc831dfc2822947
[bbaumbach/samba-autobuild/.git] / source4 / lib / replace / snprintf.c
1 /*
2  * NOTE: If you change this file, please merge it into rsync, samba, etc.
3  */
4
5 /*
6  * Copyright Patrick Powell 1995
7  * This code is based on code written by Patrick Powell (papowell@astart.com)
8  * It may be used for any purpose as long as this notice remains intact
9  * on all source code distributions
10  */
11
12 /**************************************************************
13  * Original:
14  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
15  * A bombproof version of doprnt (dopr) included.
16  * Sigh.  This sort of thing is always nasty do deal with.  Note that
17  * the version here does not include floating point...
18  *
19  * snprintf() is used instead of sprintf() as it does limit checks
20  * for string length.  This covers a nasty loophole.
21  *
22  * The other functions are there to prevent NULL pointers from
23  * causing nast effects.
24  *
25  * More Recently:
26  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
27  *  This was ugly.  It is still ugly.  I opted out of floating point
28  *  numbers, but the formatter understands just about everything
29  *  from the normal C string format, at least as far as I can tell from
30  *  the Solaris 2.5 printf(3S) man page.
31  *
32  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
33  *    Ok, added some minimal floating point support, which means this
34  *    probably requires libm on most operating systems.  Don't yet
35  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
36  *    was pretty badly broken, it just wasn't being exercised in ways
37  *    which showed it, so that's been fixed.  Also, formated the code
38  *    to mutt conventions, and removed dead code left over from the
39  *    original.  Also, there is now a builtin-test, just compile with:
40  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
41  *    and run snprintf for results.
42  * 
43  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
44  *    The PGP code was using unsigned hexadecimal formats. 
45  *    Unfortunately, unsigned formats simply didn't work.
46  *
47  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
48  *    The original code assumed that both snprintf() and vsnprintf() were
49  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
50  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
51  *
52  *  Andrew Tridgell (tridge@samba.org) Oct 1998
53  *    fixed handling of %.0f
54  *    added test for HAVE_LONG_DOUBLE
55  *
56  * tridge@samba.org, idra@samba.org, April 2001
57  *    got rid of fcvt code (twas buggy and made testing harder)
58  *    added C99 semantics
59  *
60  * date: 2002/12/19 19:56:31;  author: herb;  state: Exp;  lines: +2 -0
61  * actually print args for %g and %e
62  * 
63  * date: 2002/06/03 13:37:52;  author: jmcd;  state: Exp;  lines: +8 -0
64  * Since includes.h isn't included here, VA_COPY has to be defined here.  I don't
65  * see any include file that is guaranteed to be here, so I'm defining it
66  * locally.  Fixes AIX and Solaris builds.
67  * 
68  * date: 2002/06/03 03:07:24;  author: tridge;  state: Exp;  lines: +5 -13
69  * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
70  * functions
71  * 
72  * date: 2002/05/17 14:51:22;  author: jmcd;  state: Exp;  lines: +21 -4
73  * Fix usage of va_list passed as an arg.  Use __va_copy before using it
74  * when it exists.
75  * 
76  * date: 2002/04/16 22:38:04;  author: idra;  state: Exp;  lines: +20 -14
77  * Fix incorrect zpadlen handling in fmtfp.
78  * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
79  * few mods to make it easier to compile the tests.
80  * addedd the "Ollie" test to the floating point ones.
81  *
82  * Martin Pool (mbp@samba.org) April 2003
83  *    Remove NO_CONFIG_H so that the test case can be built within a source
84  *    tree with less trouble.
85  *    Remove unnecessary SAFE_FREE() definition.
86  *
87  * Martin Pool (mbp@samba.org) May 2003
88  *    Put in a prototype for dummy_snprintf() to quiet compiler warnings.
89  *
90  *    Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
91  *    if the C library has some snprintf functions already.
92  *
93  * Darren Tucker (dtucker@zip.com.au) 2005
94  *    Fix bug allowing read overruns of the source string with "%.*s"
95  *    Usually harmless unless the read runs outside the process' allocation
96  *    (eg if your malloc does guard pages) in which case it will segfault.
97  *    From OpenSSH.  Also added test for same.
98  *
99  * Simo Sorce (idra@samba.org) Jan 2006
100  * 
101  *    Add support for position independent parameters 
102  *    fix fmtstr now it conforms to sprintf wrt min.max
103  *
104  **************************************************************/
105
106 #include "replace.h"
107
108 #ifdef TEST_SNPRINTF /* need math library headers for testing */
109
110 /* In test mode, we pretend that this system doesn't have any snprintf
111  * functions, regardless of what config.h says. */
112 #  undef HAVE_SNPRINTF
113 #  undef HAVE_VSNPRINTF
114 #  undef HAVE_C99_VSNPRINTF
115 #  undef HAVE_ASPRINTF
116 #  undef HAVE_VASPRINTF
117 #  include <math.h>
118 #endif /* TEST_SNPRINTF */
119
120 #ifdef HAVE_STRING_H
121 #include <string.h>
122 #endif
123
124 #ifdef HAVE_STRINGS_H
125 #include <strings.h>
126 #endif
127 #ifdef HAVE_CTYPE_H
128 #include <ctype.h>
129 #endif
130 #include <sys/types.h>
131 #include <stdarg.h>
132 #ifdef HAVE_STDLIB_H
133 #include <stdlib.h>
134 #endif
135
136 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
137 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
138 #include <stdio.h>
139  /* make the compiler happy with an empty file */
140  void dummy_snprintf(void);
141  void dummy_snprintf(void) {} 
142 #endif /* HAVE_SNPRINTF, etc */
143
144 /* yes this really must be a ||. Don't muck with this (tridge) */
145 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
146
147 #ifdef HAVE_LONG_DOUBLE
148 #define LDOUBLE long double
149 #else
150 #define LDOUBLE double
151 #endif
152
153 #ifdef HAVE_LONG_LONG
154 #define LLONG long long
155 #else
156 #define LLONG long
157 #endif
158
159 #ifndef VA_COPY
160 #ifdef HAVE_VA_COPY
161 #define VA_COPY(dest, src) va_copy(dest, src)
162 #else
163 #ifdef HAVE___VA_COPY
164 #define VA_COPY(dest, src) __va_copy(dest, src)
165 #else
166 #define VA_COPY(dest, src) (dest) = (src)
167 #endif
168 #endif
169
170 /*
171  * dopr(): poor man's version of doprintf
172  */
173
174 /* format read states */
175 #define DP_S_DEFAULT 0
176 #define DP_S_FLAGS   1
177 #define DP_S_MIN     2
178 #define DP_S_DOT     3
179 #define DP_S_MAX     4
180 #define DP_S_MOD     5
181 #define DP_S_CONV    6
182 #define DP_S_DONE    7
183
184 /* format flags - Bits */
185 #define DP_F_MINUS      (1 << 0)
186 #define DP_F_PLUS       (1 << 1)
187 #define DP_F_SPACE      (1 << 2)
188 #define DP_F_NUM        (1 << 3)
189 #define DP_F_ZERO       (1 << 4)
190 #define DP_F_UP         (1 << 5)
191 #define DP_F_UNSIGNED   (1 << 6)
192
193 /* Conversion Flags */
194 #define DP_C_CHAR    1
195 #define DP_C_SHORT   2
196 #define DP_C_LONG    3
197 #define DP_C_LDOUBLE 4
198 #define DP_C_LLONG   5
199 #define DP_C_SIZET   6
200
201 /* Chunk types */
202 #define CNK_FMT_STR 0
203 #define CNK_INT     1
204 #define CNK_OCTAL   2
205 #define CNK_UINT    3
206 #define CNK_HEX     4
207 #define CNK_FLOAT   5
208 #define CNK_CHAR    6
209 #define CNK_STRING  7
210 #define CNK_PTR     8
211 #define CNK_NUM     9
212 #define CNK_PRCNT   10
213
214 #define char_to_int(p) ((p)- '0')
215 #ifndef MAX
216 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
217 #endif
218
219 struct pr_chunk {
220         int type; /* chunk type */
221         int num; /* parameter number */
222         int min; 
223         int max;
224         int flags;
225         int cflags;
226         int start;
227         int len;
228         LLONG value;
229         LDOUBLE fvalue;
230         char *strvalue;
231         void *pnum;
232         struct pr_chunk *min_star;
233         struct pr_chunk *max_star;
234         struct pr_chunk *next;
235 };
236
237 struct pr_chunk_x {
238         struct pr_chunk **chunks;
239         int num;
240 };
241
242 static size_t dopr(char *buffer, size_t maxlen, const char *format, 
243                    va_list args_in);
244 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
245                     char *value, int flags, int min, int max);
246 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
247                     LLONG value, int base, int min, int max, int flags);
248 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
249                    LDOUBLE fvalue, int min, int max, int flags);
250 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
251 static struct pr_chunk *new_chunk(void);
252 static int add_cnk_list_entry(struct pr_chunk_x **list,
253                                 int max_num, struct pr_chunk *chunk);
254
255 static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
256 {
257         char ch;
258         int state;
259         int pflag;
260         int pnum;
261         int pfirst;
262         size_t currlen;
263         va_list args;
264         const char *base;
265         struct pr_chunk *chunks = NULL;
266         struct pr_chunk *cnk = NULL;
267         struct pr_chunk_x *clist = NULL;
268         int max_pos;
269         size_t ret = -1;
270
271         VA_COPY(args, args_in);
272
273         state = DP_S_DEFAULT;
274         pfirst = 1;
275         pflag = 0;
276         pnum = 0;
277
278         max_pos = 0;
279         base = format;
280         ch = *format++;
281         
282         /* retrieve the string structure as chunks */
283         while (state != DP_S_DONE) {
284                 if (ch == '\0') 
285                         state = DP_S_DONE;
286
287                 switch(state) {
288                 case DP_S_DEFAULT:
289                         
290                         if (cnk) {
291                                 cnk->next = new_chunk();
292                                 cnk = cnk->next;
293                         } else {
294                                 cnk = new_chunk();
295                         }
296                         if (!cnk) goto done;
297                         if (!chunks) chunks = cnk;
298                         
299                         if (ch == '%') {
300                                 state = DP_S_FLAGS;
301                                 ch = *format++;
302                         } else {
303                                 cnk->type = CNK_FMT_STR;
304                                 cnk->start = format - base -1;
305                                 while ((ch != '\0') && (ch != '%')) ch = *format++;
306                                 cnk->len = format - base - cnk->start -1;
307                         }
308                         break;
309                 case DP_S_FLAGS:
310                         switch (ch) {
311                         case '-':
312                                 cnk->flags |= DP_F_MINUS;
313                                 ch = *format++;
314                                 break;
315                         case '+':
316                                 cnk->flags |= DP_F_PLUS;
317                                 ch = *format++;
318                                 break;
319                         case ' ':
320                                 cnk->flags |= DP_F_SPACE;
321                                 ch = *format++;
322                                 break;
323                         case '#':
324                                 cnk->flags |= DP_F_NUM;
325                                 ch = *format++;
326                                 break;
327                         case '0':
328                                 cnk->flags |= DP_F_ZERO;
329                                 ch = *format++;
330                                 break;
331                         case 'I':
332                                 /* internationalization not supported yet */
333                                 ch = *format++;
334                                 break;
335                         default:
336                                 state = DP_S_MIN;
337                                 break;
338                         }
339                         break;
340                 case DP_S_MIN:
341                         if (isdigit((unsigned char)ch)) {
342                                 cnk->min = 10 * cnk->min + char_to_int (ch);
343                                 ch = *format++;
344                         } else if (ch == '$') {
345                                 if (!pfirst && !pflag) {
346                                         /* parameters must be all positioned or none */
347                                         goto done;
348                                 }
349                                 if (pfirst) {
350                                         pfirst = 0;
351                                         pflag = 1;
352                                 }
353                                 if (cnk->min == 0) /* what ?? */
354                                         goto done;
355                                 cnk->num = cnk->min;
356                                 cnk->min = 0;
357                                 ch = *format++;
358                         } else if (ch == '*') {
359                                 if (pfirst) pfirst = 0;
360                                 cnk->min_star = new_chunk();
361                                 if (!cnk->min_star) /* out of memory :-( */
362                                         goto done;
363                                 cnk->min_star->type = CNK_INT;
364                                 if (pflag) {
365                                         int num;
366                                         ch = *format++;
367                                         if (!isdigit((unsigned char)ch)) {
368                                                 /* parameters must be all positioned or none */
369                                                 goto done;
370                                         }
371                                         for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
372                                                 num = 10 * num + char_to_int(ch);
373                                         }
374                                         cnk->min_star->num = num;
375                                         if (ch != '$') /* what ?? */
376                                                 goto done;
377                                 } else {
378                                         cnk->min_star->num = ++pnum;
379                                 }
380                                 max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star);
381                                 if (max_pos == 0) /* out of memory :-( */
382                                         goto done;
383                                 ch = *format++;
384                                 state = DP_S_DOT;
385                         } else {
386                                 if (pfirst) pfirst = 0;
387                                 state = DP_S_DOT;
388                         }
389                         break;
390                 case DP_S_DOT:
391                         if (ch == '.') {
392                                 state = DP_S_MAX;
393                                 ch = *format++;
394                         } else { 
395                                 state = DP_S_MOD;
396                         }
397                         break;
398                 case DP_S_MAX:
399                         if (isdigit((unsigned char)ch)) {
400                                 if (cnk->max < 0)
401                                         cnk->max = 0;
402                                 cnk->max = 10 * cnk->max + char_to_int (ch);
403                                 ch = *format++;
404                         } else if (ch == '$') {
405                                 if (!pfirst && !pflag) {
406                                         /* parameters must be all positioned or none */
407                                         goto done;
408                                 }
409                                 if (cnk->max <= 0) /* what ?? */
410                                         goto done;
411                                 cnk->num = cnk->max;
412                                 cnk->max = -1;
413                                 ch = *format++;
414                         } else if (ch == '*') {
415                                 cnk->max_star = new_chunk();
416                                 if (!cnk->max_star) /* out of memory :-( */
417                                         goto done;
418                                 cnk->max_star->type = CNK_INT;
419                                 if (pflag) {
420                                         int num;
421                                         ch = *format++;
422                                         if (!isdigit((unsigned char)ch)) {
423                                                 /* parameters must be all positioned or none */
424                                                 goto done;
425                                         }
426                                         for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
427                                                 num = 10 * num + char_to_int(ch);
428                                         }
429                                         cnk->max_star->num = num;
430                                         if (ch != '$') /* what ?? */
431                                                 goto done;
432                                 } else {
433                                         cnk->max_star->num = ++pnum;
434                                 }
435                                 max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star);
436                                 if (max_pos == 0) /* out of memory :-( */
437                                         goto done;
438
439                                 ch = *format++;
440                                 state = DP_S_MOD;
441                         } else {
442                                 state = DP_S_MOD;
443                         }
444                         break;
445                 case DP_S_MOD:
446                         switch (ch) {
447                         case 'h':
448                                 cnk->cflags = DP_C_SHORT;
449                                 ch = *format++;
450                                 if (ch == 'h') {
451                                         cnk->cflags = DP_C_CHAR;
452                                         ch = *format++;
453                                 }
454                                 break;
455                         case 'l':
456                                 cnk->cflags = DP_C_LONG;
457                                 ch = *format++;
458                                 if (ch == 'l') {        /* It's a long long */
459                                         cnk->cflags = DP_C_LLONG;
460                                         ch = *format++;
461                                 }
462                                 break;
463                         case 'L':
464                                 cnk->cflags = DP_C_LDOUBLE;
465                                 ch = *format++;
466                                 break;
467                         case 'z':
468                                 cnk->cflags = DP_C_SIZET;
469                                 ch = *format++;
470                                 break;
471                         default:
472                                 break;
473                         }
474                         state = DP_S_CONV;
475                         break;
476                 case DP_S_CONV:
477                         if (cnk->num == 0) cnk->num = ++pnum;
478                         max_pos = add_cnk_list_entry(&clist, max_pos, cnk);
479                         if (max_pos == 0) /* out of memory :-( */
480                                 goto done;
481                         
482                         switch (ch) {
483                         case 'd':
484                         case 'i':
485                                 cnk->type = CNK_INT;
486                                 break;
487                         case 'o':
488                                 cnk->type = CNK_OCTAL;
489                                 cnk->flags |= DP_F_UNSIGNED;
490                                 break;
491                         case 'u':
492                                 cnk->type = CNK_UINT;
493                                 cnk->flags |= DP_F_UNSIGNED;
494                                 break;
495                         case 'X':
496                                 cnk->flags |= DP_F_UP;
497                         case 'x':
498                                 cnk->type = CNK_HEX;
499                                 cnk->flags |= DP_F_UNSIGNED;
500                                 break;
501                         case 'A':
502                                 /* hex float not supported yet */
503                         case 'E':
504                         case 'G':
505                         case 'F':
506                                 cnk->flags |= DP_F_UP;
507                         case 'a':
508                                 /* hex float not supported yet */
509                         case 'e':
510                         case 'f':
511                         case 'g':
512                                 cnk->type = CNK_FLOAT;
513                                 break;
514                         case 'c':
515                                 cnk->type = CNK_CHAR;
516                                 break;
517                         case 's':
518                                 cnk->type = CNK_STRING;
519                                 break;
520                         case 'p':
521                                 cnk->type = CNK_PTR;
522                                 break;
523                         case 'n':
524                                 cnk->type = CNK_NUM;
525                                 break;
526                         case '%':
527                                 cnk->type = CNK_PRCNT;
528                                 break;
529                         default:
530                                 /* Unknown, bail out*/
531                                 goto done;
532                         }
533                         ch = *format++;
534                         state = DP_S_DEFAULT;
535                         break;
536                 case DP_S_DONE:
537                         break;
538                 default:
539                         /* hmm? */
540                         break; /* some picky compilers need this */
541                 }
542         }
543
544         /* retieve the format arguments */
545         for (pnum = 0; pnum < max_pos; pnum++) {
546                 int i;
547
548                 if (clist[pnum].num == 0) {
549                         /* ignoring a parameter should not be permitted
550                          * all parameters must be matched at least once
551                          * BUT seem some system ignore this rule ...
552                          * at least my glibc based system does --SSS
553                          */
554 #ifdef DEBUG_SNPRINTF
555                         printf("parameter at position %d not used\n", pnum+1);
556 #endif
557                         /* eat the parameter */
558                         va_arg (args, int);
559                         continue;
560                 }
561                 for (i = 1; i < clist[pnum].num; i++) {
562                         if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) {
563                                 /* nooo noo no!
564                                  * all the references to a parameter
565                                  * must be of the same type
566                                  */
567                                 goto done;
568                         }
569                 }
570                 cnk = clist[pnum].chunks[0];
571                 switch (cnk->type) {
572                 case CNK_INT:
573                         if (cnk->cflags == DP_C_SHORT) 
574                                 cnk->value = va_arg (args, int);
575                         else if (cnk->cflags == DP_C_LONG)
576                                 cnk->value = va_arg (args, long int);
577                         else if (cnk->cflags == DP_C_LLONG)
578                                 cnk->value = va_arg (args, LLONG);
579                         else if (cnk->cflags == DP_C_SIZET)
580                                 cnk->value = va_arg (args, ssize_t);
581                         else
582                                 cnk->value = va_arg (args, int);
583
584                         for (i = 1; i < clist[pnum].num; i++) {
585                                 clist[pnum].chunks[i]->value = cnk->value;
586                         }
587                         break;
588
589                 case CNK_OCTAL:
590                 case CNK_UINT:
591                 case CNK_HEX:
592                         if (cnk->cflags == DP_C_SHORT)
593                                 cnk->value = va_arg (args, unsigned int);
594                         else if (cnk->cflags == DP_C_LONG)
595                                 cnk->value = (unsigned long int)va_arg (args, unsigned long int);
596                         else if (cnk->cflags == DP_C_LLONG)
597                                 cnk->value = (LLONG)va_arg (args, unsigned LLONG);
598                         else if (cnk->cflags == DP_C_SIZET)
599                                 cnk->value = (size_t)va_arg (args, size_t);
600                         else
601                                 cnk->value = (unsigned int)va_arg (args, unsigned int);
602
603                         for (i = 1; i < clist[pnum].num; i++) {
604                                 clist[pnum].chunks[i]->value = cnk->value;
605                         }
606                         break;
607
608                 case CNK_FLOAT:
609                         if (cnk->cflags == DP_C_LDOUBLE)
610                                 cnk->fvalue = va_arg (args, LDOUBLE);
611                         else
612                                 cnk->fvalue = va_arg (args, double);
613
614                         for (i = 1; i < clist[pnum].num; i++) {
615                                 clist[pnum].chunks[i]->fvalue = cnk->fvalue;
616                         }
617                         break;
618
619                 case CNK_CHAR:
620                         cnk->value = va_arg (args, int);
621
622                         for (i = 1; i < clist[pnum].num; i++) {
623                                 clist[pnum].chunks[i]->value = cnk->value;
624                         }
625                         break;
626
627                 case CNK_STRING:
628                         cnk->strvalue = va_arg (args, char *);
629                         if (!cnk->strvalue) cnk->strvalue = "(NULL)";
630
631                         for (i = 1; i < clist[pnum].num; i++) {
632                                 clist[pnum].chunks[i]->strvalue = cnk->strvalue;
633                         }
634                         break;
635
636                 case CNK_PTR:
637                         cnk->strvalue = va_arg (args, void *);
638                         for (i = 1; i < clist[pnum].num; i++) {
639                                 clist[pnum].chunks[i]->strvalue = cnk->strvalue;
640                         }
641                         break;
642
643                 case CNK_NUM:
644                         if (cnk->cflags == DP_C_CHAR)
645                                 cnk->pnum = va_arg (args, char *);
646                         else if (cnk->cflags == DP_C_SHORT)
647                                 cnk->pnum = va_arg (args, short int *);
648                         else if (cnk->cflags == DP_C_LONG)
649                                 cnk->pnum = va_arg (args, long int *);
650                         else if (cnk->cflags == DP_C_LLONG)
651                                 cnk->pnum = va_arg (args, LLONG *);
652                         else if (cnk->cflags == DP_C_SIZET)
653                                 cnk->pnum = va_arg (args, ssize_t *);
654                         else
655                                 cnk->pnum = va_arg (args, int *);
656
657                         for (i = 1; i < clist[pnum].num; i++) {
658                                 clist[pnum].chunks[i]->pnum = cnk->pnum;
659                         }
660                         break;
661
662                 case CNK_PRCNT:
663                         break;
664
665                 default:
666                         /* what ?? */
667                         goto done;
668                 }
669         }
670         /* print out the actual string from chunks */
671         currlen = 0;
672         cnk = chunks;
673         while (cnk) {
674                 int len, min, max;
675
676                 if (cnk->min_star) min = cnk->min_star->value;
677                 else min = cnk->min;
678                 if (cnk->max_star) max = cnk->max_star->value;
679                 else max = cnk->max;
680
681                 switch (cnk->type) {
682
683                 case CNK_FMT_STR:
684                         if (maxlen != 0 && maxlen > currlen) {
685                                 if (maxlen > (currlen + cnk->len)) len = cnk->len;
686                                 else len = maxlen - currlen;
687
688                                 memcpy(&(buffer[currlen]), &(base[cnk->start]), len);
689                         }
690                         currlen += cnk->len;
691                                 
692                         break;
693
694                 case CNK_INT:
695                 case CNK_UINT:
696                         fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags);
697                         break;
698
699                 case CNK_OCTAL:
700                         fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags);
701                         break;
702
703                 case CNK_HEX:
704                         fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags);
705                         break;
706
707                 case CNK_FLOAT:
708                         fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags);
709                         break;
710
711                 case CNK_CHAR:
712                         dopr_outch (buffer, &currlen, maxlen, cnk->value);
713                         break;
714
715                 case CNK_STRING:
716                         if (max == -1) {
717                                 max = strlen(cnk->strvalue);
718                         }
719                         fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max);
720                         break;
721
722                 case CNK_PTR:
723                         fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags);
724                         break;
725
726                 case CNK_NUM:
727                         if (cnk->cflags == DP_C_CHAR)
728                                 *((char *)(cnk->pnum)) = (char)currlen;
729                         else if (cnk->cflags == DP_C_SHORT)
730                                 *((short int *)(cnk->pnum)) = (short int)currlen;
731                         else if (cnk->cflags == DP_C_LONG)
732                                 *((long int *)(cnk->pnum)) = (long int)currlen;
733                         else if (cnk->cflags == DP_C_LLONG)
734                                 *((LLONG *)(cnk->pnum)) = (LLONG)currlen;
735                         else if (cnk->cflags == DP_C_SIZET)
736                                 *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen;
737                         else
738                                 *((int *)(cnk->pnum)) = (int)currlen;
739                         break;
740
741                 case CNK_PRCNT:
742                         dopr_outch (buffer, &currlen, maxlen, '%');
743                         break;
744
745                 default:
746                         /* what ?? */
747                         goto done;
748                 }
749                 cnk = cnk->next;
750         }
751         if (maxlen != 0) {
752                 if (currlen < maxlen - 1) 
753                         buffer[currlen] = '\0';
754                 else if (maxlen > 0) 
755                         buffer[maxlen - 1] = '\0';
756         }
757         ret = currlen;
758
759 done:
760         while (chunks) {
761                 cnk = chunks->next;
762                 free(chunks);
763                 chunks = cnk;
764         }
765         if (clist) {
766                 for (pnum = 0; pnum < max_pos; pnum++) {
767                         if (clist[pnum].chunks) free(clist[pnum].chunks);
768                 }
769                 free(clist);
770         }
771         return ret;
772 }
773
774 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
775                     char *value, int flags, int min, int max)
776 {
777         int padlen, strln;     /* amount to pad */
778         int cnt = 0;
779
780 #ifdef DEBUG_SNPRINTF
781         printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
782 #endif
783         if (value == 0) {
784                 value = "<NULL>";
785         }
786
787         for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */
788         padlen = min - strln;
789         if (padlen < 0) 
790                 padlen = 0;
791         if (flags & DP_F_MINUS) 
792                 padlen = -padlen; /* Left Justify */
793         
794         while (padlen > 0) {
795                 dopr_outch (buffer, currlen, maxlen, ' ');
796                 --padlen;
797         }
798         while (*value && (cnt < max)) {
799                 dopr_outch (buffer, currlen, maxlen, *value++);
800                 ++cnt;
801         }
802         while (padlen < 0) {
803                 dopr_outch (buffer, currlen, maxlen, ' ');
804                 ++padlen;
805         }
806 }
807
808 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
809
810 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
811                     LLONG value, int base, int min, int max, int flags)
812 {
813         int signvalue = 0;
814         unsigned LLONG uvalue;
815         char convert[20];
816         int place = 0;
817         int spadlen = 0; /* amount to space pad */
818         int zpadlen = 0; /* amount to zero pad */
819         int caps = 0;
820         
821         if (max < 0)
822                 max = 0;
823         
824         uvalue = value;
825         
826         if(!(flags & DP_F_UNSIGNED)) {
827                 if( value < 0 ) {
828                         signvalue = '-';
829                         uvalue = -value;
830                 } else {
831                         if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
832                                 signvalue = '+';
833                         else if (flags & DP_F_SPACE)
834                                 signvalue = ' ';
835                 }
836         }
837   
838         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
839
840         do {
841                 convert[place++] =
842                         (caps? "0123456789ABCDEF":"0123456789abcdef")
843                         [uvalue % (unsigned)base  ];
844                 uvalue = (uvalue / (unsigned)base );
845         } while(uvalue && (place < 20));
846         if (place == 20) place--;
847         convert[place] = 0;
848
849         zpadlen = max - place;
850         spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
851         if (zpadlen < 0) zpadlen = 0;
852         if (spadlen < 0) spadlen = 0;
853         if (flags & DP_F_ZERO) {
854                 zpadlen = MAX(zpadlen, spadlen);
855                 spadlen = 0;
856         }
857         if (flags & DP_F_MINUS) 
858                 spadlen = -spadlen; /* Left Justifty */
859
860 #ifdef DEBUG_SNPRINTF
861         printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
862                zpadlen, spadlen, min, max, place);
863 #endif
864
865         /* Spaces */
866         while (spadlen > 0) {
867                 dopr_outch (buffer, currlen, maxlen, ' ');
868                 --spadlen;
869         }
870
871         /* Sign */
872         if (signvalue) 
873                 dopr_outch (buffer, currlen, maxlen, signvalue);
874
875         /* Zeros */
876         if (zpadlen > 0) {
877                 while (zpadlen > 0) {
878                         dopr_outch (buffer, currlen, maxlen, '0');
879                         --zpadlen;
880                 }
881         }
882
883         /* Digits */
884         while (place > 0) 
885                 dopr_outch (buffer, currlen, maxlen, convert[--place]);
886   
887         /* Left Justified spaces */
888         while (spadlen < 0) {
889                 dopr_outch (buffer, currlen, maxlen, ' ');
890                 ++spadlen;
891         }
892 }
893
894 static LDOUBLE abs_val(LDOUBLE value)
895 {
896         LDOUBLE result = value;
897
898         if (value < 0)
899                 result = -value;
900         
901         return result;
902 }
903
904 static LDOUBLE POW10(int exp)
905 {
906         LDOUBLE result = 1;
907         
908         while (exp) {
909                 result *= 10;
910                 exp--;
911         }
912   
913         return result;
914 }
915
916 static LLONG ROUND(LDOUBLE value)
917 {
918         LLONG intpart;
919
920         intpart = (LLONG)value;
921         value = value - intpart;
922         if (value >= 0.5) intpart++;
923         
924         return intpart;
925 }
926
927 /* a replacement for modf that doesn't need the math library. Should
928    be portable, but slow */
929 static double my_modf(double x0, double *iptr)
930 {
931         int i;
932         LLONG l;
933         double x = x0;
934         double f = 1.0;
935
936         for (i=0;i<100;i++) {
937                 l = (long)x;
938                 if (l <= (x+1) && l >= (x-1)) break;
939                 x *= 0.1;
940                 f *= 10.0;
941         }
942
943         if (i == 100) {
944                 /* yikes! the number is beyond what we can handle. What do we do? */
945                 (*iptr) = 0;
946                 return 0;
947         }
948
949         if (i != 0) {
950                 double i2;
951                 double ret;
952
953                 ret = my_modf(x0-l*f, &i2);
954                 (*iptr) = l*f + i2;
955                 return ret;
956         } 
957
958         (*iptr) = l;
959         return x - (*iptr);
960 }
961
962
963 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
964                    LDOUBLE fvalue, int min, int max, int flags)
965 {
966         int signvalue = 0;
967         double ufvalue;
968         char iconvert[311];
969         char fconvert[311];
970         int iplace = 0;
971         int fplace = 0;
972         int padlen = 0; /* amount to pad */
973         int zpadlen = 0; 
974         int caps = 0;
975         int idx;
976         double intpart;
977         double fracpart;
978         double temp;
979   
980         /* 
981          * AIX manpage says the default is 0, but Solaris says the default
982          * is 6, and sprintf on AIX defaults to 6
983          */
984         if (max < 0)
985                 max = 6;
986
987         ufvalue = abs_val (fvalue);
988
989         if (fvalue < 0) {
990                 signvalue = '-';
991         } else {
992                 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
993                         signvalue = '+';
994                 } else {
995                         if (flags & DP_F_SPACE)
996                                 signvalue = ' ';
997                 }
998         }
999
1000 #if 0
1001         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
1002 #endif
1003
1004 #if 0
1005          if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
1006 #endif
1007
1008         /* 
1009          * Sorry, we only support 9 digits past the decimal because of our 
1010          * conversion method
1011          */
1012         if (max > 9)
1013                 max = 9;
1014
1015         /* We "cheat" by converting the fractional part to integer by
1016          * multiplying by a factor of 10
1017          */
1018
1019         temp = ufvalue;
1020         my_modf(temp, &intpart);
1021
1022         fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
1023         
1024         if (fracpart >= POW10(max)) {
1025                 intpart++;
1026                 fracpart -= POW10(max);
1027         }
1028
1029
1030         /* Convert integer part */
1031         do {
1032                 temp = intpart*0.1;
1033                 my_modf(temp, &intpart);
1034                 idx = (int) ((temp -intpart +0.05)* 10.0);
1035                 /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
1036                 /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
1037                 iconvert[iplace++] =
1038                         (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
1039         } while (intpart && (iplace < 311));
1040         if (iplace == 311) iplace--;
1041         iconvert[iplace] = 0;
1042
1043         /* Convert fractional part */
1044         if (fracpart)
1045         {
1046                 do {
1047                         temp = fracpart*0.1;
1048                         my_modf(temp, &fracpart);
1049                         idx = (int) ((temp -fracpart +0.05)* 10.0);
1050                         /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
1051                         /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
1052                         fconvert[fplace++] =
1053                         (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
1054                 } while(fracpart && (fplace < 311));
1055                 if (fplace == 311) fplace--;
1056         }
1057         fconvert[fplace] = 0;
1058   
1059         /* -1 for decimal point, another -1 if we are printing a sign */
1060         padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
1061         zpadlen = max - fplace;
1062         if (zpadlen < 0) zpadlen = 0;
1063         if (padlen < 0) 
1064                 padlen = 0;
1065         if (flags & DP_F_MINUS) 
1066                 padlen = -padlen; /* Left Justifty */
1067         
1068         if ((flags & DP_F_ZERO) && (padlen > 0)) {
1069                 if (signvalue) {
1070                         dopr_outch (buffer, currlen, maxlen, signvalue);
1071                         --padlen;
1072                         signvalue = 0;
1073                 }
1074                 while (padlen > 0) {
1075                         dopr_outch (buffer, currlen, maxlen, '0');
1076                         --padlen;
1077                 }
1078         }
1079         while (padlen > 0) {
1080                 dopr_outch (buffer, currlen, maxlen, ' ');
1081                 --padlen;
1082         }
1083         if (signvalue) 
1084                 dopr_outch (buffer, currlen, maxlen, signvalue);
1085         
1086         while (iplace > 0) 
1087                 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
1088
1089 #ifdef DEBUG_SNPRINTF
1090         printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
1091 #endif
1092
1093         /*
1094          * Decimal point.  This should probably use locale to find the correct
1095          * char to print out.
1096          */
1097         if (max > 0) {
1098                 dopr_outch (buffer, currlen, maxlen, '.');
1099                 
1100                 while (zpadlen > 0) {
1101                         dopr_outch (buffer, currlen, maxlen, '0');
1102                         --zpadlen;
1103                 }
1104
1105                 while (fplace > 0) 
1106                         dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
1107         }
1108
1109         while (padlen < 0) {
1110                 dopr_outch (buffer, currlen, maxlen, ' ');
1111                 ++padlen;
1112         }
1113 }
1114
1115 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
1116 {
1117         if (*currlen < maxlen) {
1118                 buffer[(*currlen)] = c;
1119         }
1120         (*currlen)++;
1121 }
1122
1123 static struct pr_chunk *new_chunk(void) {
1124         struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk));
1125
1126         if (!new_c)
1127                 return NULL;
1128
1129         new_c->type = 0;
1130         new_c->num = 0;
1131         new_c->min = 0;
1132         new_c->min_star = NULL;
1133         new_c->max = -1;
1134         new_c->max_star = NULL;
1135         new_c->flags = 0;
1136         new_c->cflags = 0;
1137         new_c->start = 0;
1138         new_c->len = 0;
1139         new_c->value = 0;
1140         new_c->fvalue = 0;
1141         new_c->strvalue = NULL;
1142         new_c->pnum = NULL;
1143         new_c->next = NULL;
1144
1145         return new_c;
1146 }
1147
1148 static int add_cnk_list_entry(struct pr_chunk_x **list,
1149                                 int max_num, struct pr_chunk *chunk) {
1150         struct pr_chunk_x *l;
1151         struct pr_chunk **c;
1152         int max;
1153         int cnum;
1154         int i, pos;
1155
1156         if (chunk->num > max_num) {
1157                 max = chunk->num;
1158         
1159                 if (*list == NULL) {
1160                         l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max);
1161                         pos = 0;
1162                 } else {
1163                         l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max);
1164                         pos = max_num;
1165                 }
1166                 if (l == NULL) {
1167                         for (i = 0; i < max; i++) {
1168                                 if ((*list)[i].chunks) free((*list)[i].chunks);
1169                         }
1170                         return 0;
1171                 }
1172                 for (i = pos; i < max; i++) {
1173                         l[i].chunks = NULL;
1174                         l[i].num = 0;
1175                 }
1176         } else {
1177                 l = *list;
1178                 max = max_num;
1179         }
1180
1181         i = chunk->num - 1;
1182         cnum = l[i].num + 1;
1183         if (l[i].chunks == NULL) {
1184                 c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum); 
1185         } else {
1186                 c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum);
1187         }
1188         if (c == NULL) {
1189                 for (i = 0; i < max; i++) {
1190                         if (l[i].chunks) free(l[i].chunks);
1191                 }
1192                 return 0;
1193         }
1194         c[l[i].num] = chunk;
1195         l[i].chunks = c;
1196         l[i].num = cnum;
1197
1198         *list = l;
1199         return max;
1200 }
1201
1202  int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
1203 {
1204         return dopr(str, count, fmt, args);
1205 }
1206 #endif
1207
1208 /* yes this really must be a ||. Don't muck with this (tridge)
1209  *
1210  * The logic for these two is that we need our own definition if the
1211  * OS *either* has no definition of *sprintf, or if it does have one
1212  * that doesn't work properly according to the autoconf test.
1213  */
1214 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
1215  int snprintf(char *str,size_t count,const char *fmt,...)
1216 {
1217         size_t ret;
1218         va_list ap;
1219     
1220         va_start(ap, fmt);
1221         ret = vsnprintf(str, count, fmt, ap);
1222         va_end(ap);
1223         return ret;
1224 }
1225 #endif
1226
1227 #endif 
1228
1229 #ifndef HAVE_VASPRINTF
1230  int vasprintf(char **ptr, const char *format, va_list ap)
1231 {
1232         int ret;
1233         va_list ap2;
1234
1235         VA_COPY(ap2, ap);
1236         
1237         ret = vsnprintf(NULL, 0, format, ap2);
1238         if (ret <= 0) return ret;
1239
1240         (*ptr) = (char *)malloc(ret+1);
1241         if (!*ptr) return -1;
1242
1243         VA_COPY(ap2, ap);
1244
1245         ret = vsnprintf(*ptr, ret+1, format, ap2);
1246
1247         return ret;
1248 }
1249 #endif
1250
1251
1252 #ifndef HAVE_ASPRINTF
1253  int asprintf(char **ptr, const char *format, ...)
1254 {
1255         va_list ap;
1256         int ret;
1257         
1258         *ptr = NULL;
1259         va_start(ap, format);
1260         ret = vasprintf(ptr, format, ap);
1261         va_end(ap);
1262
1263         return ret;
1264 }
1265 #endif
1266
1267 #ifdef TEST_SNPRINTF
1268
1269  int sprintf(char *str,const char *fmt,...);
1270  int printf(const char *fmt,...);
1271
1272  int main (void)
1273 {
1274         char buf1[1024];
1275         char buf2[1024];
1276         char *buf3;
1277         char *fp_fmt[] = {
1278                 "%1.1f",
1279                 "%-1.5f",
1280                 "%1.5f",
1281                 "%123.9f",
1282                 "%10.5f",
1283                 "% 10.5f",
1284                 "%+22.9f",
1285                 "%+4.9f",
1286                 "%01.3f",
1287                 "%4f",
1288                 "%3.1f",
1289                 "%3.2f",
1290                 "%.0f",
1291                 "%f",
1292                 "%-8.8f",
1293                 "%-9.9f",
1294                 NULL
1295         };
1296         double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996, 
1297                              0.9996, 1.996, 4.136, 5.030201, 0.00205,
1298                              /* END LIST */ 0};
1299         char *int_fmt[] = {
1300                 "%-1.5d",
1301                 "%1.5d",
1302                 "%123.9d",
1303                 "%5.5d",
1304                 "%10.5d",
1305                 "% 10.5d",
1306                 "%+22.33d",
1307                 "%01.3d",
1308                 "%4d",
1309                 "%d",
1310                 NULL
1311         };
1312         long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0};
1313         char *str_fmt[] = {
1314                 "%10.5s",
1315                 "%-10.5s",
1316                 "%5.10s",
1317                 "%-5.10s",
1318                 "%10.1s",
1319                 "%0.10s",
1320                 "%10.0s",
1321                 "%1.10s",
1322                 "%s",
1323                 "%.1s",
1324                 "%.10s",
1325                 "%10s",
1326                 NULL
1327         };
1328         char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
1329 #ifdef HAVE_LONG_LONG
1330         char *ll_fmt[] = {
1331                 "%llu",
1332                 NULL
1333         };
1334         LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0};
1335 #endif
1336         int x, y;
1337         int fail = 0;
1338         int num = 0;
1339         int l1, l2;
1340         char *ss_fmt[] = {
1341                 "%zd",
1342                 "%zu",
1343                 NULL
1344         };
1345         size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0};
1346
1347         printf ("Testing snprintf format codes against system sprintf...\n");
1348
1349         for (x = 0; fp_fmt[x] ; x++) {
1350                 for (y = 0; fp_nums[y] != 0 ; y++) {
1351                         buf1[0] = buf2[0] = '\0';
1352                         l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
1353                         l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]);
1354                         buf1[1023] = buf2[1023] = '\0';
1355                         if (strcmp (buf1, buf2) || (l1 != l2)) {
1356                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
1357                                        fp_fmt[x], l1, buf1, l2, buf2);
1358                                 fail++;
1359                         }
1360                         num++;
1361                 }
1362         }
1363
1364         for (x = 0; int_fmt[x] ; x++) {
1365                 for (y = 0; int_nums[y] != 0 ; y++) {
1366                         buf1[0] = buf2[0] = '\0';
1367                         l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
1368                         l2 = sprintf (buf2, int_fmt[x], int_nums[y]);
1369                         buf1[1023] = buf2[1023] = '\0';
1370                         if (strcmp (buf1, buf2) || (l1 != l2)) {
1371                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
1372                                        int_fmt[x], l1, buf1, l2, buf2);
1373                                 fail++;
1374                         }
1375                         num++;
1376                 }
1377         }
1378
1379         for (x = 0; str_fmt[x] ; x++) {
1380                 for (y = 0; str_vals[y] != 0 ; y++) {
1381                         buf1[0] = buf2[0] = '\0';
1382                         l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
1383                         l2 = sprintf (buf2, str_fmt[x], str_vals[y]);
1384                         buf1[1023] = buf2[1023] = '\0';
1385                         if (strcmp (buf1, buf2) || (l1 != l2)) {
1386                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
1387                                        str_fmt[x], l1, buf1, l2, buf2);
1388                                 fail++;
1389                         }
1390                         num++;
1391                 }
1392         }
1393
1394 #ifdef HAVE_LONG_LONG
1395         for (x = 0; ll_fmt[x] ; x++) {
1396                 for (y = 0; ll_nums[y] != 0 ; y++) {
1397                         buf1[0] = buf2[0] = '\0';
1398                         l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
1399                         l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]);
1400                         buf1[1023] = buf2[1023] = '\0';
1401                         if (strcmp (buf1, buf2) || (l1 != l2)) {
1402                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
1403                                        ll_fmt[x], l1, buf1, l2, buf2);
1404                                 fail++;
1405                         }
1406                         num++;
1407                 }
1408         }
1409 #endif
1410
1411 #define BUFSZ 2048
1412
1413         buf1[0] = buf2[0] = '\0';
1414         if ((buf3 = malloc(BUFSZ)) == NULL) {
1415                 fail++;
1416         } else {
1417                 num++;
1418                 memset(buf3, 'a', BUFSZ);
1419                 snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3);
1420                 buf1[1023] = '\0';
1421                 if (strcmp(buf1, "a") != 0) {
1422                         printf("length limit buf1 '%s' expected 'a'\n", buf1);
1423                         fail++;
1424                 }
1425         }
1426
1427         buf1[0] = buf2[0] = '\0';
1428         l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
1429         l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
1430         buf1[1023] = buf2[1023] = '\0';
1431         if (strcmp(buf1, buf2) || (l1 != l2)) {
1432                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1433                                 "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
1434                 fail++;
1435         }
1436
1437         buf1[0] = buf2[0] = '\0';
1438         l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
1439         l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
1440         buf1[1023] = buf2[1023] = '\0';
1441         if (strcmp(buf1, buf2)) {
1442                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1443                                 "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
1444                 fail++;
1445         }
1446
1447         for (x = 0; ss_fmt[x] ; x++) {
1448                 for (y = 0; ss_nums[y] != 0 ; y++) {
1449                         buf1[0] = buf2[0] = '\0';
1450                         l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]);
1451                         l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]);
1452                         buf1[1023] = buf2[1023] = '\0';
1453                         if (strcmp (buf1, buf2) || (l1 != l2)) {
1454                                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
1455                                        ss_fmt[x], l1, buf1, l2, buf2);
1456                                 fail++;
1457                         }
1458                         num++;
1459                 }
1460         }
1461 #if 0
1462         buf1[0] = buf2[0] = '\0';
1463         l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890);
1464         l2 = sprintf(buf2, "%lld", (LLONG)1234567890);
1465         buf1[1023] = buf2[1023] = '\0';
1466         if (strcmp(buf1, buf2)) {
1467                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1468                                 "%lld", l1, buf1, l2, buf2);
1469                 fail++;
1470         }
1471
1472         buf1[0] = buf2[0] = '\0';
1473         l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123);
1474         l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123);
1475         buf1[1023] = buf2[1023] = '\0';
1476         if (strcmp(buf1, buf2)) {
1477                 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1478                                 "%Lf", l1, buf1, l2, buf2);
1479                 fail++;
1480         }
1481 #endif
1482         printf ("%d tests failed out of %d.\n", fail, num);
1483
1484         printf("seeing how many digits we support\n");
1485         {
1486                 double v0 = 0.12345678901234567890123456789012345678901;
1487                 for (x=0; x<100; x++) {
1488                         double p = pow(10, x); 
1489                         double r = v0*p;
1490                         snprintf(buf1, sizeof(buf1), "%1.1f", r);
1491                         sprintf(buf2,                "%1.1f", r);
1492                         if (strcmp(buf1, buf2)) {
1493                                 printf("we seem to support %d digits\n", x-1);
1494                                 break;
1495                         }
1496                 }
1497         }
1498
1499         return 0;
1500 }
1501 #endif /* TEST_SNPRINTF */