- neue Library "ngportab" begonnen: diese bildet Funktionen nach, die auf
[ngircd-alex.git] / src / portab / vsnprintf.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4  *
5  * Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
6  * der GNU General Public License (GPL), wie von der Free Software Foundation
7  * herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
8  * der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
9  * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
10  * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
11  *
12  * $Id: vsnprintf.c,v 1.1 2002/05/19 01:17:40 alex Exp $
13  *
14  * vsnprintf.c: vsnprintf()-Ersatz
15  */
16
17
18 #include "portab.h"
19 #include "imp.h"
20
21 #include "exp.h"
22
23
24 /*
25  * snprintf.c: Copyright Patrick Powell 1995
26  * This code is based on code written by Patrick Powell (papowell@astart.com)
27  * It may be used for any purpose as long as this notice remains intact
28  * on all source code distributions
29  *
30  * Original: 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  * tridge@samba.org, idra@samba.org, April 2001
73  *    got rid of fcvt code (twas buggy and made testing harder)
74  *    added C99 semantics
75  */
76
77
78 #ifdef HAVE_STRING_H
79 #include <string.h>
80 #endif
81 #ifdef HAVE_STRINGS_H
82 #include <strings.h>
83 #endif
84 #ifdef HAVE_CTYPE_H
85 #include <ctype.h>
86 #endif
87 #include <sys/types.h>
88 #include <stdarg.h>
89 #ifdef HAVE_STDLIB_H
90 #include <stdlib.h>
91 #endif
92
93 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
94 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
95 #include <stdio.h>
96  /* make the compiler happy with an empty file */
97  void dummy_snprintf(void) {} 
98 #else
99
100 #ifdef HAVE_LONG_DOUBLE
101 #define LDOUBLE long double
102 #else
103 #define LDOUBLE double
104 #endif
105
106 #ifdef HAVE_LONG_LONG
107 #define LLONG long long
108 #else
109 #define LLONG long
110 #endif
111
112 static size_t dopr(char *buffer, size_t maxlen, const char *format, 
113                    va_list args);
114 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
115                     char *value, int flags, int min, int max);
116 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
117                     long value, int base, int min, int max, int flags);
118 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
119                    LDOUBLE fvalue, int min, int max, int flags);
120 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
121
122 /*
123  * dopr(): poor man's version of doprintf
124  */
125
126 /* format read states */
127 #define DP_S_DEFAULT 0
128 #define DP_S_FLAGS   1
129 #define DP_S_MIN     2
130 #define DP_S_DOT     3
131 #define DP_S_MAX     4
132 #define DP_S_MOD     5
133 #define DP_S_CONV    6
134 #define DP_S_DONE    7
135
136 /* format flags - Bits */
137 #define DP_F_MINUS      (1 << 0)
138 #define DP_F_PLUS       (1 << 1)
139 #define DP_F_SPACE      (1 << 2)
140 #define DP_F_NUM        (1 << 3)
141 #define DP_F_ZERO       (1 << 4)
142 #define DP_F_UP  (1 << 5)
143 #define DP_F_UNSIGNED   (1 << 6)
144
145 /* Conversion Flags */
146 #define DP_C_SHORT   1
147 #define DP_C_LONG    2
148 #define DP_C_LDOUBLE 3
149 #define DP_C_LLONG   4
150
151 #define char_to_int(p) ((p)- '0')
152 #ifndef MAX
153 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
154 #endif
155
156 static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args)
157 {
158         char ch;
159         LLONG value;
160         LDOUBLE fvalue;
161         char *strvalue;
162         int min;
163         int max;
164         int state;
165         int flags;
166         int cflags;
167         size_t currlen;
168         
169         state = DP_S_DEFAULT;
170         currlen = flags = cflags = min = 0;
171         max = -1;
172         ch = *format++;
173         
174         while (state != DP_S_DONE) {
175                 if (ch == '\0') 
176                         state = DP_S_DONE;
177
178                 switch(state) {
179                 case DP_S_DEFAULT:
180                         if (ch == '%') 
181                                 state = DP_S_FLAGS;
182                         else 
183                                 dopr_outch (buffer, &currlen, maxlen, ch);
184                         ch = *format++;
185                         break;
186                 case DP_S_FLAGS:
187                         switch (ch) {
188                         case '-':
189                                 flags |= DP_F_MINUS;
190                                 ch = *format++;
191                                 break;
192                         case '+':
193                                 flags |= DP_F_PLUS;
194                                 ch = *format++;
195                                 break;
196                         case ' ':
197                                 flags |= DP_F_SPACE;
198                                 ch = *format++;
199                                 break;
200                         case '#':
201                                 flags |= DP_F_NUM;
202                                 ch = *format++;
203                                 break;
204                         case '0':
205                                 flags |= DP_F_ZERO;
206                                 ch = *format++;
207                                 break;
208                         default:
209                                 state = DP_S_MIN;
210                                 break;
211                         }
212                         break;
213                 case DP_S_MIN:
214                         if (isdigit((unsigned char)ch)) {
215                                 min = 10*min + char_to_int (ch);
216                                 ch = *format++;
217                         } else if (ch == '*') {
218                                 min = va_arg (args, int);
219                                 ch = *format++;
220                                 state = DP_S_DOT;
221                         } else {
222                                 state = DP_S_DOT;
223                         }
224                         break;
225                 case DP_S_DOT:
226                         if (ch == '.') {
227                                 state = DP_S_MAX;
228                                 ch = *format++;
229                         } else { 
230                                 state = DP_S_MOD;
231                         }
232                         break;
233                 case DP_S_MAX:
234                         if (isdigit((unsigned char)ch)) {
235                                 if (max < 0)
236                                         max = 0;
237                                 max = 10*max + char_to_int (ch);
238                                 ch = *format++;
239                         } else if (ch == '*') {
240                                 max = va_arg (args, int);
241                                 ch = *format++;
242                                 state = DP_S_MOD;
243                         } else {
244                                 state = DP_S_MOD;
245                         }
246                         break;
247                 case DP_S_MOD:
248                         switch (ch) {
249                         case 'h':
250                                 cflags = DP_C_SHORT;
251                                 ch = *format++;
252                                 break;
253                         case 'l':
254                                 cflags = DP_C_LONG;
255                                 ch = *format++;
256                                 if (ch == 'l') {        /* It's a long long */
257                                         cflags = DP_C_LLONG;
258                                         ch = *format++;
259                                 }
260                                 break;
261                         case 'L':
262                                 cflags = DP_C_LDOUBLE;
263                                 ch = *format++;
264                                 break;
265                         default:
266                                 break;
267                         }
268                         state = DP_S_CONV;
269                         break;
270                 case DP_S_CONV:
271                         switch (ch) {
272                         case 'd':
273                         case 'i':
274                                 if (cflags == DP_C_SHORT) 
275                                         value = va_arg (args, int);
276                                 else if (cflags == DP_C_LONG)
277                                         value = va_arg (args, long int);
278                                 else if (cflags == DP_C_LLONG)
279                                         value = va_arg (args, LLONG);
280                                 else
281                                         value = va_arg (args, int);
282                                 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
283                                 break;
284                         case 'o':
285                                 flags |= DP_F_UNSIGNED;
286                                 if (cflags == DP_C_SHORT)
287                                         value = va_arg (args, unsigned int);
288                                 else if (cflags == DP_C_LONG)
289                                         value = (long)va_arg (args, unsigned long int);
290                                 else if (cflags == DP_C_LLONG)
291                                         value = (long)va_arg (args, unsigned LLONG);
292                                 else
293                                         value = (long)va_arg (args, unsigned int);
294                                 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
295                                 break;
296                         case 'u':
297                                 flags |= DP_F_UNSIGNED;
298                                 if (cflags == DP_C_SHORT)
299                                         value = va_arg (args, unsigned int);
300                                 else if (cflags == DP_C_LONG)
301                                         value = (long)va_arg (args, unsigned long int);
302                                 else if (cflags == DP_C_LLONG)
303                                         value = (LLONG)va_arg (args, unsigned LLONG);
304                                 else
305                                         value = (long)va_arg (args, unsigned int);
306                                 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
307                                 break;
308                         case 'X':
309                                 flags |= DP_F_UP;
310                         case 'x':
311                                 flags |= DP_F_UNSIGNED;
312                                 if (cflags == DP_C_SHORT)
313                                         value = va_arg (args, unsigned int);
314                                 else if (cflags == DP_C_LONG)
315                                         value = (long)va_arg (args, unsigned long int);
316                                 else if (cflags == DP_C_LLONG)
317                                         value = (LLONG)va_arg (args, unsigned LLONG);
318                                 else
319                                         value = (long)va_arg (args, unsigned int);
320                                 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
321                                 break;
322                         case 'f':
323                                 if (cflags == DP_C_LDOUBLE)
324                                         fvalue = va_arg (args, LDOUBLE);
325                                 else
326                                         fvalue = va_arg (args, double);
327                                 /* um, floating point? */
328                                 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
329                                 break;
330                         case 'E':
331                                 flags |= DP_F_UP;
332                         case 'e':
333                                 if (cflags == DP_C_LDOUBLE)
334                                         fvalue = va_arg (args, LDOUBLE);
335                                 else
336                                         fvalue = va_arg (args, double);
337                                 break;
338                         case 'G':
339                                 flags |= DP_F_UP;
340                         case 'g':
341                                 if (cflags == DP_C_LDOUBLE)
342                                         fvalue = va_arg (args, LDOUBLE);
343                                 else
344                                         fvalue = va_arg (args, double);
345                                 break;
346                         case 'c':
347                                 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
348                                 break;
349                         case 's':
350                                 strvalue = va_arg (args, char *);
351                                 if (max == -1) {
352                                         max = strlen(strvalue);
353                                 }
354                                 if (min > 0 && max >= 0 && min > max) max = min;
355                                 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
356                                 break;
357                         case 'p':
358                                 strvalue = va_arg (args, void *);
359                                 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
360                                 break;
361                         case 'n':
362                                 if (cflags == DP_C_SHORT) {
363                                         short int *num;
364                                         num = va_arg (args, short int *);
365                                         *num = currlen;
366                                 } else if (cflags == DP_C_LONG) {
367                                         long int *num;
368                                         num = va_arg (args, long int *);
369                                         *num = (long int)currlen;
370                                 } else if (cflags == DP_C_LLONG) {
371                                         LLONG *num;
372                                         num = va_arg (args, LLONG *);
373                                         *num = (LLONG)currlen;
374                                 } else {
375                                         int *num;
376                                         num = va_arg (args, int *);
377                                         *num = currlen;
378                                 }
379                                 break;
380                         case '%':
381                                 dopr_outch (buffer, &currlen, maxlen, ch);
382                                 break;
383                         case 'w':
384                                 /* not supported yet, treat as next char */
385                                 ch = *format++;
386                                 break;
387                         default:
388                                 /* Unknown, skip */
389                                 break;
390                         }
391                         ch = *format++;
392                         state = DP_S_DEFAULT;
393                         flags = cflags = min = 0;
394                         max = -1;
395                         break;
396                 case DP_S_DONE:
397                         break;
398                 default:
399                         /* hmm? */
400                         break; /* some picky compilers need this */
401                 }
402         }
403         if (maxlen != 0) {
404                 if (currlen < maxlen - 1) 
405                         buffer[currlen] = '\0';
406                 else if (maxlen > 0) 
407                         buffer[maxlen - 1] = '\0';
408         }
409         
410         return currlen;
411 }
412
413 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
414                     char *value, int flags, int min, int max)
415 {
416         int padlen, strln;     /* amount to pad */
417         int cnt = 0;
418
419 #ifdef DEBUG_SNPRINTF
420         printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
421 #endif
422         if (value == 0) {
423                 value = "<NULL>";
424         }
425
426         for (strln = 0; value[strln]; ++strln); /* strlen */
427         padlen = min - strln;
428         if (padlen < 0) 
429                 padlen = 0;
430         if (flags & DP_F_MINUS) 
431                 padlen = -padlen; /* Left Justify */
432         
433         while ((padlen > 0) && (cnt < max)) {
434                 dopr_outch (buffer, currlen, maxlen, ' ');
435                 --padlen;
436                 ++cnt;
437         }
438         while (*value && (cnt < max)) {
439                 dopr_outch (buffer, currlen, maxlen, *value++);
440                 ++cnt;
441         }
442         while ((padlen < 0) && (cnt < max)) {
443                 dopr_outch (buffer, currlen, maxlen, ' ');
444                 ++padlen;
445                 ++cnt;
446         }
447 }
448
449 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
450
451 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
452                     long value, int base, int min, int max, int flags)
453 {
454         int signvalue = 0;
455         unsigned long uvalue;
456         char convert[20];
457         int place = 0;
458         int spadlen = 0; /* amount to space pad */
459         int zpadlen = 0; /* amount to zero pad */
460         int caps = 0;
461         
462         if (max < 0)
463                 max = 0;
464         
465         uvalue = value;
466         
467         if(!(flags & DP_F_UNSIGNED)) {
468                 if( value < 0 ) {
469                         signvalue = '-';
470                         uvalue = -value;
471                 } else {
472                         if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
473                                 signvalue = '+';
474                         else if (flags & DP_F_SPACE)
475                                 signvalue = ' ';
476                 }
477         }
478   
479         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
480
481         do {
482                 convert[place++] =
483                         (caps? "0123456789ABCDEF":"0123456789abcdef")
484                         [uvalue % (unsigned)base  ];
485                 uvalue = (uvalue / (unsigned)base );
486         } while(uvalue && (place < 20));
487         if (place == 20) place--;
488         convert[place] = 0;
489
490         zpadlen = max - place;
491         spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
492         if (zpadlen < 0) zpadlen = 0;
493         if (spadlen < 0) spadlen = 0;
494         if (flags & DP_F_ZERO) {
495                 zpadlen = MAX(zpadlen, spadlen);
496                 spadlen = 0;
497         }
498         if (flags & DP_F_MINUS) 
499                 spadlen = -spadlen; /* Left Justifty */
500
501 #ifdef DEBUG_SNPRINTF
502         printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
503                zpadlen, spadlen, min, max, place);
504 #endif
505
506         /* Spaces */
507         while (spadlen > 0) {
508                 dopr_outch (buffer, currlen, maxlen, ' ');
509                 --spadlen;
510         }
511
512         /* Sign */
513         if (signvalue) 
514                 dopr_outch (buffer, currlen, maxlen, signvalue);
515
516         /* Zeros */
517         if (zpadlen > 0) {
518                 while (zpadlen > 0) {
519                         dopr_outch (buffer, currlen, maxlen, '0');
520                         --zpadlen;
521                 }
522         }
523
524         /* Digits */
525         while (place > 0) 
526                 dopr_outch (buffer, currlen, maxlen, convert[--place]);
527   
528         /* Left Justified spaces */
529         while (spadlen < 0) {
530                 dopr_outch (buffer, currlen, maxlen, ' ');
531                 ++spadlen;
532         }
533 }
534
535 static LDOUBLE abs_val(LDOUBLE value)
536 {
537         LDOUBLE result = value;
538
539         if (value < 0)
540                 result = -value;
541         
542         return result;
543 }
544
545 static LDOUBLE POW10(int exp)
546 {
547         LDOUBLE result = 1;
548         
549         while (exp) {
550                 result *= 10;
551                 exp--;
552         }
553   
554         return result;
555 }
556
557 static LLONG ROUND(LDOUBLE value)
558 {
559         LLONG intpart;
560
561         intpart = (LLONG)value;
562         value = value - intpart;
563         if (value >= 0.5) intpart++;
564         
565         return intpart;
566 }
567
568 /* a replacement for modf that doesn't need the math library. Should
569    be portable, but slow */
570 static double my_modf(double x0, double *iptr)
571 {
572         int i;
573         long l;
574         double x = x0;
575         double f = 1.0;
576
577         for (i=0;i<100;i++) {
578                 l = (long)x;
579                 if (l <= (x+1) && l >= (x-1)) break;
580                 x *= 0.1;
581                 f *= 10.0;
582         }
583
584         if (i == 100) {
585                 /* yikes! the number is beyond what we can handle. What do we do? */
586                 (*iptr) = 0;
587                 return 0;
588         }
589
590         if (i != 0) {
591                 double i2;
592                 double ret;
593
594                 ret = my_modf(x0-l*f, &i2);
595                 (*iptr) = l*f + i2;
596                 return ret;
597         } 
598
599         (*iptr) = l;
600         return x - (*iptr);
601 }
602
603
604 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
605                    LDOUBLE fvalue, int min, int max, int flags)
606 {
607         int signvalue = 0;
608         double ufvalue;
609         char iconvert[311];
610         char fconvert[311];
611         int iplace = 0;
612         int fplace = 0;
613         int padlen = 0; /* amount to pad */
614         int zpadlen = 0; 
615         int caps = 0;
616         int index;
617         double intpart;
618         double fracpart;
619         double temp;
620   
621         /* 
622          * AIX manpage says the default is 0, but Solaris says the default
623          * is 6, and sprintf on AIX defaults to 6
624          */
625         if (max < 0)
626                 max = 6;
627
628         ufvalue = abs_val (fvalue);
629
630         if (fvalue < 0) {
631                 signvalue = '-';
632         } else {
633                 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
634                         signvalue = '+';
635                 } else {
636                         if (flags & DP_F_SPACE)
637                                 signvalue = ' ';
638                 }
639         }
640
641 #if 0
642         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
643 #endif
644
645 #if 0
646          if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
647 #endif
648
649         /* 
650          * Sorry, we only support 16 digits past the decimal because of our 
651          * conversion method
652          */
653         if (max > 16)
654                 max = 16;
655
656         /* We "cheat" by converting the fractional part to integer by
657          * multiplying by a factor of 10
658          */
659
660         temp = ufvalue;
661         my_modf(temp, &intpart);
662
663         fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
664         
665         if (fracpart >= POW10(max)) {
666                 intpart++;
667                 fracpart -= POW10(max);
668         }
669
670
671         /* Convert integer part */
672         do {
673                 temp = intpart;
674                 my_modf(intpart*0.1, &intpart);
675                 temp = temp*0.1;
676                 index = (int) ((temp -intpart +0.05)* 10.0);
677                 /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
678                 /* printf ("%llf, %f, %x\n", temp, intpart, index); */
679                 iconvert[iplace++] =
680                         (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
681         } while (intpart && (iplace < 311));
682         if (iplace == 311) iplace--;
683         iconvert[iplace] = 0;
684
685         /* Convert fractional part */
686         if (fracpart)
687         {
688                 do {
689                         temp = fracpart;
690                         my_modf(fracpart*0.1, &fracpart);
691                         temp = temp*0.1;
692                         index = (int) ((temp -fracpart +0.05)* 10.0);
693                         /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
694                         /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
695                         fconvert[fplace++] =
696                         (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
697                 } while(fracpart && (fplace < 311));
698                 if (fplace == 311) fplace--;
699         }
700         fconvert[fplace] = 0;
701   
702         /* -1 for decimal point, another -1 if we are printing a sign */
703         padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
704         zpadlen = max - fplace;
705         if (zpadlen < 0) zpadlen = 0;
706         if (padlen < 0) 
707                 padlen = 0;
708         if (flags & DP_F_MINUS) 
709                 padlen = -padlen; /* Left Justifty */
710         
711         if ((flags & DP_F_ZERO) && (padlen > 0)) {
712                 if (signvalue) {
713                         dopr_outch (buffer, currlen, maxlen, signvalue);
714                         --padlen;
715                         signvalue = 0;
716                 }
717                 while (padlen > 0) {
718                         dopr_outch (buffer, currlen, maxlen, '0');
719                         --padlen;
720                 }
721         }
722         while (padlen > 0) {
723                 dopr_outch (buffer, currlen, maxlen, ' ');
724                 --padlen;
725         }
726         if (signvalue) 
727                 dopr_outch (buffer, currlen, maxlen, signvalue);
728         
729         while (iplace > 0) 
730                 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
731
732 #ifdef DEBUG_SNPRINTF
733         printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
734 #endif
735
736         /*
737          * Decimal point.  This should probably use locale to find the correct
738          * char to print out.
739          */
740         if (max > 0) {
741                 dopr_outch (buffer, currlen, maxlen, '.');
742                 
743                 while (fplace > 0) 
744                         dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
745         }
746         
747         while (zpadlen > 0) {
748                 dopr_outch (buffer, currlen, maxlen, '0');
749                 --zpadlen;
750         }
751
752         while (padlen < 0) {
753                 dopr_outch (buffer, currlen, maxlen, ' ');
754                 ++padlen;
755         }
756 }
757
758 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
759 {
760         if (*currlen < maxlen) {
761                 buffer[(*currlen)] = c;
762         }
763         (*currlen)++;
764 }
765
766 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
767  int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
768 {
769         return dopr(str, count, fmt, args);
770 }
771 #endif
772
773 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
774  int snprintf(char *str,size_t count,const char *fmt,...)
775 {
776         size_t ret;
777         va_list ap;
778     
779         va_start(ap, fmt);
780         ret = vsnprintf(str, count, fmt, ap);
781         va_end(ap);
782         return ret;
783 }
784 #endif
785
786 #endif 
787
788 #ifndef HAVE_VASPRINTF
789  int vasprintf(char **ptr, const char *format, va_list ap)
790 {
791         int ret;
792         
793         ret = vsnprintf(NULL, 0, format, ap);
794         if (ret <= 0) return ret;
795
796         (*ptr) = (char *)malloc(ret+1);
797         if (!*ptr) return -1;
798         ret = vsnprintf(*ptr, ret+1, format, ap);
799
800         return ret;
801 }
802 #endif
803
804
805 #ifndef HAVE_ASPRINTF
806  int asprintf(char **ptr, const char *format, ...)
807 {
808         va_list ap;
809         int ret;
810         
811         va_start(ap, format);
812         ret = vasprintf(ptr, format, ap);
813         va_end(ap);
814
815         return ret;
816 }
817 #endif
818
819 #ifdef TEST_SNPRINTF
820 int sprintf(char *str,const char *fmt,...);
821 int main (void)
822 {
823         char buf1[1024];
824         char buf2[1024];
825         char *fp_fmt[] = {
826                 "%1.1f",
827                 "%-1.5f",
828                 "%1.5f",
829                 "%123.9f",
830                 "%10.5f",
831                 "% 10.5f",
832                 "%+22.9f",
833                 "%+4.9f",
834                 "%01.3f",
835                 "%4f",
836                 "%3.1f",
837                 "%3.2f",
838                 "%.0f",
839                 "%f",
840                 "-16.16f",
841                 NULL
842         };
843         double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
844                              0.9996, 1.996, 4.136,  0};
845         char *int_fmt[] = {
846                 "%-1.5d",
847                 "%1.5d",
848                 "%123.9d",
849                 "%5.5d",
850                 "%10.5d",
851                 "% 10.5d",
852                 "%+22.33d",
853                 "%01.3d",
854                 "%4d",
855                 "%d",
856                 NULL
857         };
858         long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
859         char *str_fmt[] = {
860                 "10.5s",
861                 "5.10s",
862                 "10.1s",
863                 "0.10s",
864                 "10.0s",
865                 "1.10s",
866                 "%s",
867                 "%.1s",
868                 "%.10s",
869                 "%10s",
870                 NULL
871         };
872         char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
873         int x, y;
874         int fail = 0;
875         int num = 0;
876
877         printf ("Testing snprintf format codes against system sprintf...\n");
878
879         for (x = 0; fp_fmt[x] ; x++) {
880                 for (y = 0; fp_nums[y] != 0 ; y++) {
881                         int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
882                         int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
883                         sprintf (buf2, fp_fmt[x], fp_nums[y]);
884                         if (strcmp (buf1, buf2)) {
885                                 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
886                                        fp_fmt[x], buf1, buf2);
887                                 fail++;
888                         }
889                         if (l1 != l2) {
890                                 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
891                                 fail++;
892                         }
893                         num++;
894                 }
895         }
896
897         for (x = 0; int_fmt[x] ; x++) {
898                 for (y = 0; int_nums[y] != 0 ; y++) {
899                         int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
900                         int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
901                         sprintf (buf2, int_fmt[x], int_nums[y]);
902                         if (strcmp (buf1, buf2)) {
903                                 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
904                                        int_fmt[x], buf1, buf2);
905                                 fail++;
906                         }
907                         if (l1 != l2) {
908                                 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
909                                 fail++;
910                         }
911                         num++;
912                 }
913         }
914
915         for (x = 0; str_fmt[x] ; x++) {
916                 for (y = 0; str_vals[y] != 0 ; y++) {
917                         int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
918                         int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
919                         sprintf (buf2, str_fmt[x], str_vals[y]);
920                         if (strcmp (buf1, buf2)) {
921                                 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", 
922                                        str_fmt[x], buf1, buf2);
923                                 fail++;
924                         }
925                         if (l1 != l2) {
926                                 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
927                                 fail++;
928                         }
929                         num++;
930                 }
931         }
932
933         printf ("%d tests failed out of %d.\n", fail, num);
934
935         printf("seeing how many digits we support\n");
936         {
937                 double v0 = 0.12345678901234567890123456789012345678901;
938                 for (x=0; x<100; x++) {
939                         snprintf(buf1, sizeof(buf1), "%1.1f", v0*pow(10, x));
940                         sprintf(buf2,           "%1.1f", v0*pow(10, x));
941                         if (strcmp(buf1, buf2)) {
942                                 printf("we seem to support %d digits\n", x-1);
943                                 break;
944                         }
945                 }
946         }
947         return 0;
948 }
949 #endif /* SNPRINTF_TEST */
950
951
952 /* -eof- */