]> arthur.barton.de Git - ngircd-alex.git/blob - src/portab/vsnprintf.c
Make vsnprintf() portab function more portable
[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 #ifdef PROTOTYPES
86 #include <stdarg.h>
87 #else
88 #include <varargs.h>
89 #endif
90 #ifdef HAVE_STDLIB_H
91 #include <stdlib.h>
92 #endif
93
94 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF)
95 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
96 #include <stdio.h>
97 /* make the compiler happy with an empty file */
98 void dummy_snprintf PARAMS(( void ));
99 void dummy_snprintf PARAMS(( void )) { }
100 #else
101
102 #ifdef HAVE_LONG_DOUBLE
103 #define LDOUBLE long double
104 #else
105 #define LDOUBLE double
106 #endif
107
108 #ifdef HAVE_LONG_LONG
109 #define LLONG long long
110 #else
111 #define LLONG long
112 #endif
113
114 static size_t dopr PARAMS((char *buffer, size_t maxlen, const char *format,
115                            va_list args));
116 static void fmtstr PARAMS((char *buffer, size_t *currlen, size_t maxlen,
117                            char *value, int flags, int min, int max));
118 static void fmtint PARAMS((char *buffer, size_t *currlen, size_t maxlen,
119                            long value, int base, int min, int max, int flags));
120 static void fmtfp PARAMS((char *buffer, size_t *currlen, size_t maxlen,
121                           LDOUBLE fvalue, int min, int max, int flags));
122 static void dopr_outch PARAMS((char *buffer, size_t *currlen, size_t maxlen,
123                                char c));
124
125 /*
126  * dopr(): poor man's version of doprintf
127  */
128
129 /* format read states */
130 #define DP_S_DEFAULT 0
131 #define DP_S_FLAGS   1
132 #define DP_S_MIN     2
133 #define DP_S_DOT     3
134 #define DP_S_MAX     4
135 #define DP_S_MOD     5
136 #define DP_S_CONV    6
137 #define DP_S_DONE    7
138
139 /* format flags - Bits */
140 #define DP_F_MINUS      (1 << 0)
141 #define DP_F_PLUS       (1 << 1)
142 #define DP_F_SPACE      (1 << 2)
143 #define DP_F_NUM        (1 << 3)
144 #define DP_F_ZERO       (1 << 4)
145 #define DP_F_UP  (1 << 5)
146 #define DP_F_UNSIGNED   (1 << 6)
147
148 /* Conversion Flags */
149 #define DP_C_SHORT   1
150 #define DP_C_LONG    2
151 #define DP_C_LDOUBLE 3
152 #define DP_C_LLONG   4
153
154 #define char_to_int(p) ((p)- '0')
155 #ifndef MAX
156 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
157 #endif
158
159 static size_t
160 dopr(char *buffer, size_t maxlen, const char *format, va_list args)
161 {
162         char ch;
163         LLONG value;
164         LDOUBLE fvalue;
165         char *strvalue;
166         int min;
167         int max;
168         int state;
169         int flags;
170         int cflags;
171         size_t currlen;
172
173         state = DP_S_DEFAULT;
174         currlen = flags = cflags = min = 0;
175         max = -1;
176         ch = *format++;
177
178         while (state != DP_S_DONE) {
179                 if (ch == '\0')
180                         state = DP_S_DONE;
181
182                 switch(state) {
183                 case DP_S_DEFAULT:
184                         if (ch == '%')
185                                 state = DP_S_FLAGS;
186                         else
187                                 dopr_outch (buffer, &currlen, maxlen, ch);
188                         ch = *format++;
189                         break;
190                 case DP_S_FLAGS:
191                         switch (ch) {
192                         case '-':
193                                 flags |= DP_F_MINUS;
194                                 ch = *format++;
195                                 break;
196                         case '+':
197                                 flags |= DP_F_PLUS;
198                                 ch = *format++;
199                                 break;
200                         case ' ':
201                                 flags |= DP_F_SPACE;
202                                 ch = *format++;
203                                 break;
204                         case '#':
205                                 flags |= DP_F_NUM;
206                                 ch = *format++;
207                                 break;
208                         case '0':
209                                 flags |= DP_F_ZERO;
210                                 ch = *format++;
211                                 break;
212                         default:
213                                 state = DP_S_MIN;
214                                 break;
215                         }
216                         break;
217                 case DP_S_MIN:
218                         if (isdigit((unsigned char)ch)) {
219                                 min = 10*min + char_to_int (ch);
220                                 ch = *format++;
221                         } else if (ch == '*') {
222                                 min = va_arg (args, int);
223                                 ch = *format++;
224                                 state = DP_S_DOT;
225                         } else {
226                                 state = DP_S_DOT;
227                         }
228                         break;
229                 case DP_S_DOT:
230                         if (ch == '.') {
231                                 state = DP_S_MAX;
232                                 ch = *format++;
233                         } else {
234                                 state = DP_S_MOD;
235                         }
236                         break;
237                 case DP_S_MAX:
238                         if (isdigit((unsigned char)ch)) {
239                                 if (max < 0)
240                                         max = 0;
241                                 max = 10*max + char_to_int (ch);
242                                 ch = *format++;
243                         } else if (ch == '*') {
244                                 max = va_arg (args, int);
245                                 ch = *format++;
246                                 state = DP_S_MOD;
247                         } else {
248                                 state = DP_S_MOD;
249                         }
250                         break;
251                 case DP_S_MOD:
252                         switch (ch) {
253                         case 'h':
254                                 cflags = DP_C_SHORT;
255                                 ch = *format++;
256                                 break;
257                         case 'l':
258                                 cflags = DP_C_LONG;
259                                 ch = *format++;
260                                 if (ch == 'l') {        /* It's a long long */
261                                         cflags = DP_C_LLONG;
262                                         ch = *format++;
263                                 }
264                                 break;
265                         case 'L':
266                                 cflags = DP_C_LDOUBLE;
267                                 ch = *format++;
268                                 break;
269                         default:
270                                 break;
271                         }
272                         state = DP_S_CONV;
273                         break;
274                 case DP_S_CONV:
275                         switch (ch) {
276                         case 'd':
277                         case 'i':
278                                 if (cflags == DP_C_SHORT)
279                                         value = va_arg (args, int);
280                                 else if (cflags == DP_C_LONG)
281                                         value = va_arg (args, long int);
282                                 else if (cflags == DP_C_LLONG)
283                                         value = va_arg (args, LLONG);
284                                 else
285                                         value = va_arg (args, int);
286                                 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
287                                 break;
288                         case 'o':
289                                 flags |= DP_F_UNSIGNED;
290                                 if (cflags == DP_C_SHORT)
291                                         value = va_arg (args, unsigned int);
292                                 else if (cflags == DP_C_LONG)
293                                         value = (long)va_arg (args, unsigned long int);
294                                 else if (cflags == DP_C_LLONG)
295                                         value = (long)va_arg (args, unsigned LLONG);
296                                 else
297                                         value = (long)va_arg (args, unsigned int);
298                                 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
299                                 break;
300                         case 'u':
301                                 flags |= DP_F_UNSIGNED;
302                                 if (cflags == DP_C_SHORT)
303                                         value = va_arg (args, unsigned int);
304                                 else if (cflags == DP_C_LONG)
305                                         value = (long)va_arg (args, unsigned long int);
306                                 else if (cflags == DP_C_LLONG)
307                                         value = (LLONG)va_arg (args, unsigned LLONG);
308                                 else
309                                         value = (long)va_arg (args, unsigned int);
310                                 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
311                                 break;
312                         case 'X':
313                                 flags |= DP_F_UP;
314                         case 'x':
315                                 flags |= DP_F_UNSIGNED;
316                                 if (cflags == DP_C_SHORT)
317                                         value = va_arg (args, unsigned int);
318                                 else if (cflags == DP_C_LONG)
319                                         value = (long)va_arg (args, unsigned long int);
320                                 else if (cflags == DP_C_LLONG)
321                                         value = (LLONG)va_arg (args, unsigned LLONG);
322                                 else
323                                         value = (long)va_arg (args, unsigned int);
324                                 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
325                                 break;
326                         case 'f':
327                                 if (cflags == DP_C_LDOUBLE)
328                                         fvalue = va_arg (args, LDOUBLE);
329                                 else
330                                         fvalue = va_arg (args, double);
331                                 /* um, floating point? */
332                                 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
333                                 break;
334                         case 'E':
335                                 flags |= DP_F_UP;
336                         case 'e':
337                                 if (cflags == DP_C_LDOUBLE)
338                                         fvalue = va_arg (args, LDOUBLE);
339                                 else
340                                         fvalue = va_arg (args, double);
341                                 break;
342                         case 'G':
343                                 flags |= DP_F_UP;
344                         case 'g':
345                                 if (cflags == DP_C_LDOUBLE)
346                                         fvalue = va_arg (args, LDOUBLE);
347                                 else
348                                         fvalue = va_arg (args, double);
349                                 break;
350                         case 'c':
351                                 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
352                                 break;
353                         case 's':
354                                 strvalue = va_arg (args, char *);
355                                 if (max == -1) {
356                                         max = strlen(strvalue);
357                                 }
358                                 if (min > 0 && max >= 0 && min > max) max = min;
359                                 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
360                                 break;
361                         case 'p':
362                                 strvalue = va_arg (args, void *);
363                                 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
364                                 break;
365                         case 'n':
366                                 if (cflags == DP_C_SHORT) {
367                                         short int *num;
368                                         num = va_arg (args, short int *);
369                                         *num = currlen;
370                                 } else if (cflags == DP_C_LONG) {
371                                         long int *num;
372                                         num = va_arg (args, long int *);
373                                         *num = (long int)currlen;
374                                 } else if (cflags == DP_C_LLONG) {
375                                         LLONG *num;
376                                         num = va_arg (args, LLONG *);
377                                         *num = (LLONG)currlen;
378                                 } else {
379                                         int *num;
380                                         num = va_arg (args, int *);
381                                         *num = currlen;
382                                 }
383                                 break;
384                         case '%':
385                                 dopr_outch (buffer, &currlen, maxlen, ch);
386                                 break;
387                         case 'w':
388                                 /* not supported yet, treat as next char */
389                                 ch = *format++;
390                                 break;
391                         default:
392                                 /* Unknown, skip */
393                                 break;
394                         }
395                         ch = *format++;
396                         state = DP_S_DEFAULT;
397                         flags = cflags = min = 0;
398                         max = -1;
399                         break;
400                 case DP_S_DONE:
401                         break;
402                 default:
403                         /* hmm? */
404                         break; /* some picky compilers need this */
405                 }
406         }
407         if (maxlen != 0) {
408                 if (currlen < maxlen - 1)
409                         buffer[currlen] = '\0';
410                 else if (maxlen > 0)
411                         buffer[maxlen - 1] = '\0';
412         }
413
414         return currlen;
415 }
416
417 static void
418 fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags,
419        int min, int max)
420 {
421         int padlen, strln;     /* amount to pad */
422         int cnt = 0;
423
424 #ifdef DEBUG_SNPRINTF
425         printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
426 #endif
427         if (value == 0) {
428                 value = "<NULL>";
429         }
430
431         for (strln = 0; value[strln]; ++strln); /* strlen */
432         padlen = min - strln;
433         if (padlen < 0)
434                 padlen = 0;
435         if (flags & DP_F_MINUS)
436                 padlen = -padlen; /* Left Justify */
437
438         while ((padlen > 0) && (cnt < max)) {
439                 dopr_outch (buffer, currlen, maxlen, ' ');
440                 --padlen;
441                 ++cnt;
442         }
443         while (*value && (cnt < max)) {
444                 dopr_outch (buffer, currlen, maxlen, *value++);
445                 ++cnt;
446         }
447         while ((padlen < 0) && (cnt < max)) {
448                 dopr_outch (buffer, currlen, maxlen, ' ');
449                 ++padlen;
450                 ++cnt;
451         }
452 }
453
454 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
455
456 static void
457 fmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base,
458        int min, int max, int flags)
459 {
460         int signvalue = 0;
461         unsigned long uvalue;
462         char convert[20];
463         int place = 0;
464         int spadlen = 0; /* amount to space pad */
465         int zpadlen = 0; /* amount to zero pad */
466         int caps = 0;
467
468         if (max < 0)
469                 max = 0;
470
471         uvalue = value;
472
473         if(!(flags & DP_F_UNSIGNED)) {
474                 if( value < 0 ) {
475                         signvalue = '-';
476                         uvalue = -value;
477                 } else {
478                         if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
479                                 signvalue = '+';
480                         else if (flags & DP_F_SPACE)
481                                 signvalue = ' ';
482                 }
483         }
484
485         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
486
487         do {
488                 convert[place++] =
489                         (caps? "0123456789ABCDEF":"0123456789abcdef")
490                         [uvalue % (unsigned)base  ];
491                 uvalue = (uvalue / (unsigned)base );
492         } while(uvalue && (place < 20));
493         if (place == 20) place--;
494         convert[place] = 0;
495
496         zpadlen = max - place;
497         spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
498         if (zpadlen < 0) zpadlen = 0;
499         if (spadlen < 0) spadlen = 0;
500         if (flags & DP_F_ZERO) {
501                 zpadlen = MAX(zpadlen, spadlen);
502                 spadlen = 0;
503         }
504         if (flags & DP_F_MINUS)
505                 spadlen = -spadlen; /* Left Justifty */
506
507 #ifdef DEBUG_SNPRINTF
508         printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
509                zpadlen, spadlen, min, max, place);
510 #endif
511
512         /* Spaces */
513         while (spadlen > 0) {
514                 dopr_outch (buffer, currlen, maxlen, ' ');
515                 --spadlen;
516         }
517
518         /* Sign */
519         if (signvalue)
520                 dopr_outch (buffer, currlen, maxlen, signvalue);
521
522         /* Zeros */
523         if (zpadlen > 0) {
524                 while (zpadlen > 0) {
525                         dopr_outch (buffer, currlen, maxlen, '0');
526                         --zpadlen;
527                 }
528         }
529
530         /* Digits */
531         while (place > 0)
532                 dopr_outch (buffer, currlen, maxlen, convert[--place]);
533
534         /* Left Justified spaces */
535         while (spadlen < 0) {
536                 dopr_outch (buffer, currlen, maxlen, ' ');
537                 ++spadlen;
538         }
539 }
540
541 static LDOUBLE
542 abs_val(LDOUBLE value)
543 {
544         LDOUBLE result = value;
545
546         if (value < 0)
547                 result = -value;
548
549         return result;
550 }
551
552 static LDOUBLE
553 POW10(int exp)
554 {
555         LDOUBLE result = 1;
556
557         while (exp) {
558                 result *= 10;
559                 exp--;
560         }
561
562         return result;
563 }
564
565 static LLONG
566 ROUND(LDOUBLE value)
567 {
568         LLONG intpart;
569
570         intpart = (LLONG)value;
571         value = value - intpart;
572         if (value >= 0.5) intpart++;
573
574         return intpart;
575 }
576
577 /* a replacement for modf that doesn't need the math library. Should
578    be portable, but slow */
579 static double
580 my_modf(double x0, double *iptr)
581 {
582         int i;
583         long l;
584         double x = x0;
585         double f = 1.0;
586
587         for (i=0;i<100;i++) {
588                 l = (long)x;
589                 if (l <= (x+1) && l >= (x-1)) break;
590                 x *= 0.1;
591                 f *= 10.0;
592         }
593
594         if (i == 100) {
595                 /* yikes! the number is beyond what we can handle. What do we do? */
596                 (*iptr) = 0;
597                 return 0;
598         }
599
600         if (i != 0) {
601                 double i2;
602                 double ret;
603
604                 ret = my_modf(x0-l*f, &i2);
605                 (*iptr) = l*f + i2;
606                 return ret;
607         }
608
609         (*iptr) = l;
610         return x - (*iptr);
611 }
612
613
614 static void
615 fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue,
616        int min, int max, int flags)
617 {
618         int signvalue = 0;
619         double ufvalue;
620         char iconvert[311];
621         char fconvert[311];
622         int iplace = 0;
623         int fplace = 0;
624         int padlen = 0; /* amount to pad */
625         int zpadlen = 0;
626         int caps = 0;
627         int index;
628         double intpart;
629         double fracpart;
630         double temp;
631
632         /*
633          * AIX manpage says the default is 0, but Solaris says the default
634          * is 6, and sprintf on AIX defaults to 6
635          */
636         if (max < 0)
637                 max = 6;
638
639         ufvalue = abs_val (fvalue);
640
641         if (fvalue < 0) {
642                 signvalue = '-';
643         } else {
644                 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
645                         signvalue = '+';
646                 } else {
647                         if (flags & DP_F_SPACE)
648                                 signvalue = ' ';
649                 }
650         }
651
652         /*
653          * Sorry, we only support 16 digits past the decimal because of our
654          * conversion method
655          */
656         if (max > 16)
657                 max = 16;
658
659         /* We "cheat" by converting the fractional part to integer by
660          * multiplying by a factor of 10
661          */
662
663         temp = ufvalue;
664         my_modf(temp, &intpart);
665
666         fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
667
668         if (fracpart >= POW10(max)) {
669                 intpart++;
670                 fracpart -= POW10(max);
671         }
672
673
674         /* Convert integer part */
675         do {
676                 temp = intpart;
677                 my_modf(intpart*0.1, &intpart);
678                 temp = temp*0.1;
679                 index = (int) ((temp -intpart +0.05)* 10.0);
680                 /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
681                 /* printf ("%llf, %f, %x\n", temp, intpart, index); */
682                 iconvert[iplace++] =
683                         (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
684         } while (intpart && (iplace < 311));
685         if (iplace == 311) iplace--;
686         iconvert[iplace] = 0;
687
688         /* Convert fractional part */
689         if (fracpart)
690         {
691                 do {
692                         temp = fracpart;
693                         my_modf(fracpart*0.1, &fracpart);
694                         temp = temp*0.1;
695                         index = (int) ((temp -fracpart +0.05)* 10.0);
696                         /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
697                         /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
698                         fconvert[fplace++] =
699                         (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
700                 } while(fracpart && (fplace < 311));
701                 if (fplace == 311) fplace--;
702         }
703         fconvert[fplace] = 0;
704
705         /* -1 for decimal point, another -1 if we are printing a sign */
706         padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
707         zpadlen = max - fplace;
708         if (zpadlen < 0) zpadlen = 0;
709         if (padlen < 0)
710                 padlen = 0;
711         if (flags & DP_F_MINUS)
712                 padlen = -padlen; /* Left Justifty */
713
714         if ((flags & DP_F_ZERO) && (padlen > 0)) {
715                 if (signvalue) {
716                         dopr_outch (buffer, currlen, maxlen, signvalue);
717                         --padlen;
718                         signvalue = 0;
719                 }
720                 while (padlen > 0) {
721                         dopr_outch (buffer, currlen, maxlen, '0');
722                         --padlen;
723                 }
724         }
725         while (padlen > 0) {
726                 dopr_outch (buffer, currlen, maxlen, ' ');
727                 --padlen;
728         }
729         if (signvalue)
730                 dopr_outch (buffer, currlen, maxlen, signvalue);
731
732         while (iplace > 0)
733                 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
734
735 #ifdef DEBUG_SNPRINTF
736         printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
737 #endif
738
739         /*
740          * Decimal point.  This should probably use locale to find the correct
741          * char to print out.
742          */
743         if (max > 0) {
744                 dopr_outch (buffer, currlen, maxlen, '.');
745
746                 while (fplace > 0)
747                         dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
748         }
749
750         while (zpadlen > 0) {
751                 dopr_outch (buffer, currlen, maxlen, '0');
752                 --zpadlen;
753         }
754
755         while (padlen < 0) {
756                 dopr_outch (buffer, currlen, maxlen, ' ');
757                 ++padlen;
758         }
759 }
760
761 static void
762 dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
763 {
764         if (*currlen < maxlen) {
765                 buffer[(*currlen)] = c;
766         }
767         (*currlen)++;
768 }
769
770 #if !defined(HAVE_VSNPRINTF)
771 int
772 vsnprintf (char *str, size_t count, const char *fmt, va_list args)
773 {
774         return dopr(str, count, fmt, args);
775 }
776 #endif
777
778 #if !defined(HAVE_SNPRINTF)
779 #ifdef PROTOTYPES
780 int
781 snprintf(char *str, size_t count, const char *fmt, ...)
782 #else
783 int
784 snprintf(str, count, fmt, va_alist)
785 char *str;
786 size_t count;
787 const char *fmt;
788 va_dcl
789 #endif
790 {
791         size_t ret;
792         va_list ap;
793
794         va_start(ap, fmt);
795         ret = vsnprintf(str, count, fmt, ap);
796         va_end(ap);
797         return ret;
798 }
799 #endif
800
801 #endif
802
803 /* -eof- */