]> arthur.barton.de Git - ngircd-alex.git/blob - src/portab/vsnprintf.c
843f4ff3f55f8164d0703e6e53b7c74d80ca4dc1
[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  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * snprintf() and vsnprintf() replacement functions
17  */
18
19 /*
20  * snprintf.c: Copyright Patrick Powell 1995
21  * This code is based on code written by Patrick Powell (papowell@astart.com)
22  * It may be used for any purpose as long as this notice remains intact
23  * on all source code distributions
24  *
25  * Original: Patrick Powell Tue Apr 11 09:48:21 PDT 1995
26  * A bombproof version of doprnt (dopr) included.
27  * Sigh. This sort of thing is always nasty do deal with. Note that
28  * the version here does not include floating point...
29  *
30  * snprintf() is used instead of sprintf() as it does limit checks
31  * for string length. This covers a nasty loophole.
32  *
33  * The other functions are there to prevent NULL pointers from
34  * causing nast effects.
35  *
36  * More Recently:
37  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
38  *  This was ugly. It is still ugly. I opted out of floating point
39  *  numbers, but the formatter understands just about everything
40  *  from the normal C string format, at least as far as I can tell from
41  *  the Solaris 2.5 printf(3S) man page.
42  *
43  * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
44  *  Ok, added some minimal floating point support, which means this
45  *  probably requires libm on most operating systems. Don't yet
46  *  support the exponent (e,E) and sigfig (g,G). Also, fmtint()
47  *  was pretty badly broken, it just wasn't being exercised in ways
48  *  which showed it, so that's been fixed. Also, formated the code
49  *  to mutt conventions, and removed dead code left over from the
50  *  original. Also, there is now a builtin-test, just compile with:
51  *    gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
52  *  and run snprintf for results.
53  * 
54  * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
55  *  The PGP code was using unsigned hexadecimal formats. 
56  *  Unfortunately, unsigned formats simply didn't work.
57  *
58  * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
59  *  The original code assumed that both snprintf() and vsnprintf() were
60  *  missing. Some systems only have snprintf() but not vsnprintf(), so
61  *  the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
62  *
63  * Andrew Tridgell <tridge@samba.org>, October 1998
64  *  fixed handling of %.0f
65  *  added test for HAVE_LONG_DOUBLE
66  *
67  * tridge@samba.org, idra@samba.org, April 2001
68  *  got rid of fcvt code (twas buggy and made testing harder)
69  *  added C99 semantics
70  *
71  * Alexander Barton, <alex@barton.de>, 2002-05-19
72  *  removed [v]asprintf() and C99 tests: not needed by ngIRCd.
73  */
74
75 #ifdef HAVE_STRING_H
76 #include <string.h>
77 #endif
78 #ifdef HAVE_STRINGS_H
79 #include <strings.h>
80 #endif
81 #ifdef HAVE_CTYPE_H
82 #include <ctype.h>
83 #endif
84 #include <sys/types.h>
85 #include <stdarg.h>
86 #ifdef HAVE_STDLIB_H
87 #include <stdlib.h>
88 #endif
89
90 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF)
91 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
92 #include <stdio.h>
93 /* make the compiler happy with an empty file */
94 void dummy_snprintf PARAMS(( void ));
95 void dummy_snprintf PARAMS(( void )) { }
96 #else
97
98 #ifdef HAVE_LONG_DOUBLE
99 #define LDOUBLE long double
100 #else
101 #define LDOUBLE double
102 #endif
103
104 #ifdef HAVE_LONG_LONG
105 #define LLONG long long
106 #else
107 #define LLONG long
108 #endif
109
110 static size_t dopr(char *buffer, size_t maxlen, const char *format, 
111                    va_list args);
112 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
113                     char *value, int flags, int min, int max);
114 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
115                     long value, int base, int min, int max, int flags);
116 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
117                    LDOUBLE fvalue, int min, int max, int flags);
118 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
119
120 /*
121  * dopr(): poor man's version of doprintf
122  */
123
124 /* format read states */
125 #define DP_S_DEFAULT 0
126 #define DP_S_FLAGS   1
127 #define DP_S_MIN     2
128 #define DP_S_DOT     3
129 #define DP_S_MAX     4
130 #define DP_S_MOD     5
131 #define DP_S_CONV    6
132 #define DP_S_DONE    7
133
134 /* format flags - Bits */
135 #define DP_F_MINUS      (1 << 0)
136 #define DP_F_PLUS       (1 << 1)
137 #define DP_F_SPACE      (1 << 2)
138 #define DP_F_NUM        (1 << 3)
139 #define DP_F_ZERO       (1 << 4)
140 #define DP_F_UP  (1 << 5)
141 #define DP_F_UNSIGNED   (1 << 6)
142
143 /* Conversion Flags */
144 #define DP_C_SHORT   1
145 #define DP_C_LONG    2
146 #define DP_C_LDOUBLE 3
147 #define DP_C_LLONG   4
148
149 #define char_to_int(p) ((p)- '0')
150 #ifndef MAX
151 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
152 #endif
153
154 static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args)
155 {
156         char ch;
157         LLONG value;
158         LDOUBLE fvalue;
159         char *strvalue;
160         int min;
161         int max;
162         int state;
163         int flags;
164         int cflags;
165         size_t currlen;
166         
167         state = DP_S_DEFAULT;
168         currlen = flags = cflags = min = 0;
169         max = -1;
170         ch = *format++;
171         
172         while (state != DP_S_DONE) {
173                 if (ch == '\0') 
174                         state = DP_S_DONE;
175
176                 switch(state) {
177                 case DP_S_DEFAULT:
178                         if (ch == '%') 
179                                 state = DP_S_FLAGS;
180                         else 
181                                 dopr_outch (buffer, &currlen, maxlen, ch);
182                         ch = *format++;
183                         break;
184                 case DP_S_FLAGS:
185                         switch (ch) {
186                         case '-':
187                                 flags |= DP_F_MINUS;
188                                 ch = *format++;
189                                 break;
190                         case '+':
191                                 flags |= DP_F_PLUS;
192                                 ch = *format++;
193                                 break;
194                         case ' ':
195                                 flags |= DP_F_SPACE;
196                                 ch = *format++;
197                                 break;
198                         case '#':
199                                 flags |= DP_F_NUM;
200                                 ch = *format++;
201                                 break;
202                         case '0':
203                                 flags |= DP_F_ZERO;
204                                 ch = *format++;
205                                 break;
206                         default:
207                                 state = DP_S_MIN;
208                                 break;
209                         }
210                         break;
211                 case DP_S_MIN:
212                         if (isdigit((unsigned char)ch)) {
213                                 min = 10*min + char_to_int (ch);
214                                 ch = *format++;
215                         } else if (ch == '*') {
216                                 min = va_arg (args, int);
217                                 ch = *format++;
218                                 state = DP_S_DOT;
219                         } else {
220                                 state = DP_S_DOT;
221                         }
222                         break;
223                 case DP_S_DOT:
224                         if (ch == '.') {
225                                 state = DP_S_MAX;
226                                 ch = *format++;
227                         } else { 
228                                 state = DP_S_MOD;
229                         }
230                         break;
231                 case DP_S_MAX:
232                         if (isdigit((unsigned char)ch)) {
233                                 if (max < 0)
234                                         max = 0;
235                                 max = 10*max + char_to_int (ch);
236                                 ch = *format++;
237                         } else if (ch == '*') {
238                                 max = va_arg (args, int);
239                                 ch = *format++;
240                                 state = DP_S_MOD;
241                         } else {
242                                 state = DP_S_MOD;
243                         }
244                         break;
245                 case DP_S_MOD:
246                         switch (ch) {
247                         case 'h':
248                                 cflags = DP_C_SHORT;
249                                 ch = *format++;
250                                 break;
251                         case 'l':
252                                 cflags = DP_C_LONG;
253                                 ch = *format++;
254                                 if (ch == 'l') {        /* It's a long long */
255                                         cflags = DP_C_LLONG;
256                                         ch = *format++;
257                                 }
258                                 break;
259                         case 'L':
260                                 cflags = DP_C_LDOUBLE;
261                                 ch = *format++;
262                                 break;
263                         default:
264                                 break;
265                         }
266                         state = DP_S_CONV;
267                         break;
268                 case DP_S_CONV:
269                         switch (ch) {
270                         case 'd':
271                         case 'i':
272                                 if (cflags == DP_C_SHORT) 
273                                         value = va_arg (args, int);
274                                 else if (cflags == DP_C_LONG)
275                                         value = va_arg (args, long int);
276                                 else if (cflags == DP_C_LLONG)
277                                         value = va_arg (args, LLONG);
278                                 else
279                                         value = va_arg (args, int);
280                                 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
281                                 break;
282                         case 'o':
283                                 flags |= DP_F_UNSIGNED;
284                                 if (cflags == DP_C_SHORT)
285                                         value = va_arg (args, unsigned int);
286                                 else if (cflags == DP_C_LONG)
287                                         value = (long)va_arg (args, unsigned long int);
288                                 else if (cflags == DP_C_LLONG)
289                                         value = (long)va_arg (args, unsigned LLONG);
290                                 else
291                                         value = (long)va_arg (args, unsigned int);
292                                 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
293                                 break;
294                         case 'u':
295                                 flags |= DP_F_UNSIGNED;
296                                 if (cflags == DP_C_SHORT)
297                                         value = va_arg (args, unsigned int);
298                                 else if (cflags == DP_C_LONG)
299                                         value = (long)va_arg (args, unsigned long int);
300                                 else if (cflags == DP_C_LLONG)
301                                         value = (LLONG)va_arg (args, unsigned LLONG);
302                                 else
303                                         value = (long)va_arg (args, unsigned int);
304                                 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
305                                 break;
306                         case 'X':
307                                 flags |= DP_F_UP;
308                         case 'x':
309                                 flags |= DP_F_UNSIGNED;
310                                 if (cflags == DP_C_SHORT)
311                                         value = va_arg (args, unsigned int);
312                                 else if (cflags == DP_C_LONG)
313                                         value = (long)va_arg (args, unsigned long int);
314                                 else if (cflags == DP_C_LLONG)
315                                         value = (LLONG)va_arg (args, unsigned LLONG);
316                                 else
317                                         value = (long)va_arg (args, unsigned int);
318                                 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
319                                 break;
320                         case 'f':
321                                 if (cflags == DP_C_LDOUBLE)
322                                         fvalue = va_arg (args, LDOUBLE);
323                                 else
324                                         fvalue = va_arg (args, double);
325                                 /* um, floating point? */
326                                 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
327                                 break;
328                         case 'E':
329                                 flags |= DP_F_UP;
330                         case 'e':
331                                 if (cflags == DP_C_LDOUBLE)
332                                         fvalue = va_arg (args, LDOUBLE);
333                                 else
334                                         fvalue = va_arg (args, double);
335                                 break;
336                         case 'G':
337                                 flags |= DP_F_UP;
338                         case 'g':
339                                 if (cflags == DP_C_LDOUBLE)
340                                         fvalue = va_arg (args, LDOUBLE);
341                                 else
342                                         fvalue = va_arg (args, double);
343                                 break;
344                         case 'c':
345                                 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
346                                 break;
347                         case 's':
348                                 strvalue = va_arg (args, char *);
349                                 if (max == -1) {
350                                         max = strlen(strvalue);
351                                 }
352                                 if (min > 0 && max >= 0 && min > max) max = min;
353                                 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
354                                 break;
355                         case 'p':
356                                 strvalue = va_arg (args, void *);
357                                 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
358                                 break;
359                         case 'n':
360                                 if (cflags == DP_C_SHORT) {
361                                         short int *num;
362                                         num = va_arg (args, short int *);
363                                         *num = currlen;
364                                 } else if (cflags == DP_C_LONG) {
365                                         long int *num;
366                                         num = va_arg (args, long int *);
367                                         *num = (long int)currlen;
368                                 } else if (cflags == DP_C_LLONG) {
369                                         LLONG *num;
370                                         num = va_arg (args, LLONG *);
371                                         *num = (LLONG)currlen;
372                                 } else {
373                                         int *num;
374                                         num = va_arg (args, int *);
375                                         *num = currlen;
376                                 }
377                                 break;
378                         case '%':
379                                 dopr_outch (buffer, &currlen, maxlen, ch);
380                                 break;
381                         case 'w':
382                                 /* not supported yet, treat as next char */
383                                 ch = *format++;
384                                 break;
385                         default:
386                                 /* Unknown, skip */
387                                 break;
388                         }
389                         ch = *format++;
390                         state = DP_S_DEFAULT;
391                         flags = cflags = min = 0;
392                         max = -1;
393                         break;
394                 case DP_S_DONE:
395                         break;
396                 default:
397                         /* hmm? */
398                         break; /* some picky compilers need this */
399                 }
400         }
401         if (maxlen != 0) {
402                 if (currlen < maxlen - 1) 
403                         buffer[currlen] = '\0';
404                 else if (maxlen > 0) 
405                         buffer[maxlen - 1] = '\0';
406         }
407         
408         return currlen;
409 }
410
411 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
412                     char *value, int flags, int min, int max)
413 {
414         int padlen, strln;     /* amount to pad */
415         int cnt = 0;
416
417 #ifdef DEBUG_SNPRINTF
418         printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
419 #endif
420         if (value == 0) {
421                 value = "<NULL>";
422         }
423
424         for (strln = 0; value[strln]; ++strln); /* strlen */
425         padlen = min - strln;
426         if (padlen < 0) 
427                 padlen = 0;
428         if (flags & DP_F_MINUS) 
429                 padlen = -padlen; /* Left Justify */
430         
431         while ((padlen > 0) && (cnt < max)) {
432                 dopr_outch (buffer, currlen, maxlen, ' ');
433                 --padlen;
434                 ++cnt;
435         }
436         while (*value && (cnt < max)) {
437                 dopr_outch (buffer, currlen, maxlen, *value++);
438                 ++cnt;
439         }
440         while ((padlen < 0) && (cnt < max)) {
441                 dopr_outch (buffer, currlen, maxlen, ' ');
442                 ++padlen;
443                 ++cnt;
444         }
445 }
446
447 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
448
449 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
450                     long value, int base, int min, int max, int flags)
451 {
452         int signvalue = 0;
453         unsigned long uvalue;
454         char convert[20];
455         int place = 0;
456         int spadlen = 0; /* amount to space pad */
457         int zpadlen = 0; /* amount to zero pad */
458         int caps = 0;
459         
460         if (max < 0)
461                 max = 0;
462         
463         uvalue = value;
464         
465         if(!(flags & DP_F_UNSIGNED)) {
466                 if( value < 0 ) {
467                         signvalue = '-';
468                         uvalue = -value;
469                 } else {
470                         if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
471                                 signvalue = '+';
472                         else if (flags & DP_F_SPACE)
473                                 signvalue = ' ';
474                 }
475         }
476   
477         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
478
479         do {
480                 convert[place++] =
481                         (caps? "0123456789ABCDEF":"0123456789abcdef")
482                         [uvalue % (unsigned)base  ];
483                 uvalue = (uvalue / (unsigned)base );
484         } while(uvalue && (place < 20));
485         if (place == 20) place--;
486         convert[place] = 0;
487
488         zpadlen = max - place;
489         spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
490         if (zpadlen < 0) zpadlen = 0;
491         if (spadlen < 0) spadlen = 0;
492         if (flags & DP_F_ZERO) {
493                 zpadlen = MAX(zpadlen, spadlen);
494                 spadlen = 0;
495         }
496         if (flags & DP_F_MINUS) 
497                 spadlen = -spadlen; /* Left Justifty */
498
499 #ifdef DEBUG_SNPRINTF
500         printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
501                zpadlen, spadlen, min, max, place);
502 #endif
503
504         /* Spaces */
505         while (spadlen > 0) {
506                 dopr_outch (buffer, currlen, maxlen, ' ');
507                 --spadlen;
508         }
509
510         /* Sign */
511         if (signvalue) 
512                 dopr_outch (buffer, currlen, maxlen, signvalue);
513
514         /* Zeros */
515         if (zpadlen > 0) {
516                 while (zpadlen > 0) {
517                         dopr_outch (buffer, currlen, maxlen, '0');
518                         --zpadlen;
519                 }
520         }
521
522         /* Digits */
523         while (place > 0) 
524                 dopr_outch (buffer, currlen, maxlen, convert[--place]);
525   
526         /* Left Justified spaces */
527         while (spadlen < 0) {
528                 dopr_outch (buffer, currlen, maxlen, ' ');
529                 ++spadlen;
530         }
531 }
532
533 static LDOUBLE abs_val(LDOUBLE value)
534 {
535         LDOUBLE result = value;
536
537         if (value < 0)
538                 result = -value;
539         
540         return result;
541 }
542
543 static LDOUBLE POW10(int exp)
544 {
545         LDOUBLE result = 1;
546         
547         while (exp) {
548                 result *= 10;
549                 exp--;
550         }
551   
552         return result;
553 }
554
555 static LLONG ROUND(LDOUBLE value)
556 {
557         LLONG intpart;
558
559         intpart = (LLONG)value;
560         value = value - intpart;
561         if (value >= 0.5) intpart++;
562         
563         return intpart;
564 }
565
566 /* a replacement for modf that doesn't need the math library. Should
567    be portable, but slow */
568 static double my_modf(double x0, double *iptr)
569 {
570         int i;
571         long l;
572         double x = x0;
573         double f = 1.0;
574
575         for (i=0;i<100;i++) {
576                 l = (long)x;
577                 if (l <= (x+1) && l >= (x-1)) break;
578                 x *= 0.1;
579                 f *= 10.0;
580         }
581
582         if (i == 100) {
583                 /* yikes! the number is beyond what we can handle. What do we do? */
584                 (*iptr) = 0;
585                 return 0;
586         }
587
588         if (i != 0) {
589                 double i2;
590                 double ret;
591
592                 ret = my_modf(x0-l*f, &i2);
593                 (*iptr) = l*f + i2;
594                 return ret;
595         } 
596
597         (*iptr) = l;
598         return x - (*iptr);
599 }
600
601
602 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
603                    LDOUBLE fvalue, int min, int max, int flags)
604 {
605         int signvalue = 0;
606         double ufvalue;
607         char iconvert[311];
608         char fconvert[311];
609         int iplace = 0;
610         int fplace = 0;
611         int padlen = 0; /* amount to pad */
612         int zpadlen = 0; 
613         int caps = 0;
614         int index;
615         double intpart;
616         double fracpart;
617         double temp;
618   
619         /* 
620          * AIX manpage says the default is 0, but Solaris says the default
621          * is 6, and sprintf on AIX defaults to 6
622          */
623         if (max < 0)
624                 max = 6;
625
626         ufvalue = abs_val (fvalue);
627
628         if (fvalue < 0) {
629                 signvalue = '-';
630         } else {
631                 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
632                         signvalue = '+';
633                 } else {
634                         if (flags & DP_F_SPACE)
635                                 signvalue = ' ';
636                 }
637         }
638
639         /* 
640          * Sorry, we only support 16 digits past the decimal because of our 
641          * conversion method
642          */
643         if (max > 16)
644                 max = 16;
645
646         /* We "cheat" by converting the fractional part to integer by
647          * multiplying by a factor of 10
648          */
649
650         temp = ufvalue;
651         my_modf(temp, &intpart);
652
653         fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
654         
655         if (fracpart >= POW10(max)) {
656                 intpart++;
657                 fracpart -= POW10(max);
658         }
659
660
661         /* Convert integer part */
662         do {
663                 temp = intpart;
664                 my_modf(intpart*0.1, &intpart);
665                 temp = temp*0.1;
666                 index = (int) ((temp -intpart +0.05)* 10.0);
667                 /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
668                 /* printf ("%llf, %f, %x\n", temp, intpart, index); */
669                 iconvert[iplace++] =
670                         (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
671         } while (intpart && (iplace < 311));
672         if (iplace == 311) iplace--;
673         iconvert[iplace] = 0;
674
675         /* Convert fractional part */
676         if (fracpart)
677         {
678                 do {
679                         temp = fracpart;
680                         my_modf(fracpart*0.1, &fracpart);
681                         temp = temp*0.1;
682                         index = (int) ((temp -fracpart +0.05)* 10.0);
683                         /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
684                         /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
685                         fconvert[fplace++] =
686                         (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
687                 } while(fracpart && (fplace < 311));
688                 if (fplace == 311) fplace--;
689         }
690         fconvert[fplace] = 0;
691   
692         /* -1 for decimal point, another -1 if we are printing a sign */
693         padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
694         zpadlen = max - fplace;
695         if (zpadlen < 0) zpadlen = 0;
696         if (padlen < 0) 
697                 padlen = 0;
698         if (flags & DP_F_MINUS) 
699                 padlen = -padlen; /* Left Justifty */
700         
701         if ((flags & DP_F_ZERO) && (padlen > 0)) {
702                 if (signvalue) {
703                         dopr_outch (buffer, currlen, maxlen, signvalue);
704                         --padlen;
705                         signvalue = 0;
706                 }
707                 while (padlen > 0) {
708                         dopr_outch (buffer, currlen, maxlen, '0');
709                         --padlen;
710                 }
711         }
712         while (padlen > 0) {
713                 dopr_outch (buffer, currlen, maxlen, ' ');
714                 --padlen;
715         }
716         if (signvalue) 
717                 dopr_outch (buffer, currlen, maxlen, signvalue);
718         
719         while (iplace > 0) 
720                 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
721
722 #ifdef DEBUG_SNPRINTF
723         printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
724 #endif
725
726         /*
727          * Decimal point.  This should probably use locale to find the correct
728          * char to print out.
729          */
730         if (max > 0) {
731                 dopr_outch (buffer, currlen, maxlen, '.');
732                 
733                 while (fplace > 0) 
734                         dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
735         }
736         
737         while (zpadlen > 0) {
738                 dopr_outch (buffer, currlen, maxlen, '0');
739                 --zpadlen;
740         }
741
742         while (padlen < 0) {
743                 dopr_outch (buffer, currlen, maxlen, ' ');
744                 ++padlen;
745         }
746 }
747
748 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
749 {
750         if (*currlen < maxlen) {
751                 buffer[(*currlen)] = c;
752         }
753         (*currlen)++;
754 }
755
756 #if !defined(HAVE_VSNPRINTF)
757 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
758 {
759         return dopr(str, count, fmt, args);
760 }
761 #endif
762
763 #if !defined(HAVE_SNPRINTF)
764 int snprintf(char *str,size_t count,const char *fmt,...)
765 {
766         size_t ret;
767         va_list ap;
768     
769         va_start(ap, fmt);
770         ret = vsnprintf(str, count, fmt, ap);
771         va_end(ap);
772         return ret;
773 }
774 #endif
775
776 #endif
777
778 /* -eof- */