]> arthur.barton.de Git - ngircd-alex.git/blob - src/portab/vsnprintf.c
Don't abort startup when setgid/setuid() fails with EINVAL
[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 PARAMS((char *buffer, size_t maxlen, const char *format,
111                            va_list args));
112 static void fmtstr PARAMS((char *buffer, size_t *currlen, size_t maxlen,
113                            char *value, int flags, int min, int max));
114 static void fmtint PARAMS((char *buffer, size_t *currlen, size_t maxlen,
115                            long value, int base, int min, int max, int flags));
116 static void fmtfp PARAMS((char *buffer, size_t *currlen, size_t maxlen,
117                           LDOUBLE fvalue, int min, int max, int flags));
118 static void dopr_outch PARAMS((char *buffer, size_t *currlen, size_t maxlen,
119                                char c));
120
121 /*
122  * dopr(): poor man's version of doprintf
123  */
124
125 /* format read states */
126 #define DP_S_DEFAULT 0
127 #define DP_S_FLAGS   1
128 #define DP_S_MIN     2
129 #define DP_S_DOT     3
130 #define DP_S_MAX     4
131 #define DP_S_MOD     5
132 #define DP_S_CONV    6
133 #define DP_S_DONE    7
134
135 /* format flags - Bits */
136 #define DP_F_MINUS      (1 << 0)
137 #define DP_F_PLUS       (1 << 1)
138 #define DP_F_SPACE      (1 << 2)
139 #define DP_F_NUM        (1 << 3)
140 #define DP_F_ZERO       (1 << 4)
141 #define DP_F_UP  (1 << 5)
142 #define DP_F_UNSIGNED   (1 << 6)
143
144 /* Conversion Flags */
145 #define DP_C_SHORT   1
146 #define DP_C_LONG    2
147 #define DP_C_LDOUBLE 3
148 #define DP_C_LLONG   4
149
150 #define char_to_int(p) ((p)- '0')
151 #ifndef MAX
152 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
153 #endif
154
155 static size_t
156 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
414 fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags,
415        int min, int max)
416 {
417         int padlen, strln;     /* amount to pad */
418         int cnt = 0;
419
420 #ifdef DEBUG_SNPRINTF
421         printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
422 #endif
423         if (value == 0) {
424                 value = "<NULL>";
425         }
426
427         for (strln = 0; value[strln]; ++strln); /* strlen */
428         padlen = min - strln;
429         if (padlen < 0)
430                 padlen = 0;
431         if (flags & DP_F_MINUS)
432                 padlen = -padlen; /* Left Justify */
433
434         while ((padlen > 0) && (cnt < max)) {
435                 dopr_outch (buffer, currlen, maxlen, ' ');
436                 --padlen;
437                 ++cnt;
438         }
439         while (*value && (cnt < max)) {
440                 dopr_outch (buffer, currlen, maxlen, *value++);
441                 ++cnt;
442         }
443         while ((padlen < 0) && (cnt < max)) {
444                 dopr_outch (buffer, currlen, maxlen, ' ');
445                 ++padlen;
446                 ++cnt;
447         }
448 }
449
450 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
451
452 static void
453 fmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base,
454        int min, int max, int flags)
455 {
456         int signvalue = 0;
457         unsigned long uvalue;
458         char convert[20];
459         int place = 0;
460         int spadlen = 0; /* amount to space pad */
461         int zpadlen = 0; /* amount to zero pad */
462         int caps = 0;
463
464         if (max < 0)
465                 max = 0;
466
467         uvalue = value;
468
469         if(!(flags & DP_F_UNSIGNED)) {
470                 if( value < 0 ) {
471                         signvalue = '-';
472                         uvalue = -value;
473                 } else {
474                         if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
475                                 signvalue = '+';
476                         else if (flags & DP_F_SPACE)
477                                 signvalue = ' ';
478                 }
479         }
480
481         if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
482
483         do {
484                 convert[place++] =
485                         (caps? "0123456789ABCDEF":"0123456789abcdef")
486                         [uvalue % (unsigned)base  ];
487                 uvalue = (uvalue / (unsigned)base );
488         } while(uvalue && (place < 20));
489         if (place == 20) place--;
490         convert[place] = 0;
491
492         zpadlen = max - place;
493         spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
494         if (zpadlen < 0) zpadlen = 0;
495         if (spadlen < 0) spadlen = 0;
496         if (flags & DP_F_ZERO) {
497                 zpadlen = MAX(zpadlen, spadlen);
498                 spadlen = 0;
499         }
500         if (flags & DP_F_MINUS)
501                 spadlen = -spadlen; /* Left Justifty */
502
503 #ifdef DEBUG_SNPRINTF
504         printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
505                zpadlen, spadlen, min, max, place);
506 #endif
507
508         /* Spaces */
509         while (spadlen > 0) {
510                 dopr_outch (buffer, currlen, maxlen, ' ');
511                 --spadlen;
512         }
513
514         /* Sign */
515         if (signvalue)
516                 dopr_outch (buffer, currlen, maxlen, signvalue);
517
518         /* Zeros */
519         if (zpadlen > 0) {
520                 while (zpadlen > 0) {
521                         dopr_outch (buffer, currlen, maxlen, '0');
522                         --zpadlen;
523                 }
524         }
525
526         /* Digits */
527         while (place > 0)
528                 dopr_outch (buffer, currlen, maxlen, convert[--place]);
529
530         /* Left Justified spaces */
531         while (spadlen < 0) {
532                 dopr_outch (buffer, currlen, maxlen, ' ');
533                 ++spadlen;
534         }
535 }
536
537 static LDOUBLE
538 abs_val(LDOUBLE value)
539 {
540         LDOUBLE result = value;
541
542         if (value < 0)
543                 result = -value;
544
545         return result;
546 }
547
548 static LDOUBLE
549 POW10(int exp)
550 {
551         LDOUBLE result = 1;
552
553         while (exp) {
554                 result *= 10;
555                 exp--;
556         }
557
558         return result;
559 }
560
561 static LLONG
562 ROUND(LDOUBLE value)
563 {
564         LLONG intpart;
565
566         intpart = (LLONG)value;
567         value = value - intpart;
568         if (value >= 0.5) intpart++;
569
570         return intpart;
571 }
572
573 /* a replacement for modf that doesn't need the math library. Should
574    be portable, but slow */
575 static double
576 my_modf(double x0, double *iptr)
577 {
578         int i;
579         long l;
580         double x = x0;
581         double f = 1.0;
582
583         for (i=0;i<100;i++) {
584                 l = (long)x;
585                 if (l <= (x+1) && l >= (x-1)) break;
586                 x *= 0.1;
587                 f *= 10.0;
588         }
589
590         if (i == 100) {
591                 /* yikes! the number is beyond what we can handle. What do we do? */
592                 (*iptr) = 0;
593                 return 0;
594         }
595
596         if (i != 0) {
597                 double i2;
598                 double ret;
599
600                 ret = my_modf(x0-l*f, &i2);
601                 (*iptr) = l*f + i2;
602                 return ret;
603         }
604
605         (*iptr) = l;
606         return x - (*iptr);
607 }
608
609
610 static void
611 fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue,
612        int min, int max, int flags)
613 {
614         int signvalue = 0;
615         double ufvalue;
616         char iconvert[311];
617         char fconvert[311];
618         int iplace = 0;
619         int fplace = 0;
620         int padlen = 0; /* amount to pad */
621         int zpadlen = 0;
622         int caps = 0;
623         int index;
624         double intpart;
625         double fracpart;
626         double temp;
627
628         /*
629          * AIX manpage says the default is 0, but Solaris says the default
630          * is 6, and sprintf on AIX defaults to 6
631          */
632         if (max < 0)
633                 max = 6;
634
635         ufvalue = abs_val (fvalue);
636
637         if (fvalue < 0) {
638                 signvalue = '-';
639         } else {
640                 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
641                         signvalue = '+';
642                 } else {
643                         if (flags & DP_F_SPACE)
644                                 signvalue = ' ';
645                 }
646         }
647
648         /*
649          * Sorry, we only support 16 digits past the decimal because of our
650          * conversion method
651          */
652         if (max > 16)
653                 max = 16;
654
655         /* We "cheat" by converting the fractional part to integer by
656          * multiplying by a factor of 10
657          */
658
659         temp = ufvalue;
660         my_modf(temp, &intpart);
661
662         fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
663
664         if (fracpart >= POW10(max)) {
665                 intpart++;
666                 fracpart -= POW10(max);
667         }
668
669
670         /* Convert integer part */
671         do {
672                 temp = intpart;
673                 my_modf(intpart*0.1, &intpart);
674                 temp = temp*0.1;
675                 index = (int) ((temp -intpart +0.05)* 10.0);
676                 /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
677                 /* printf ("%llf, %f, %x\n", temp, intpart, index); */
678                 iconvert[iplace++] =
679                         (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
680         } while (intpart && (iplace < 311));
681         if (iplace == 311) iplace--;
682         iconvert[iplace] = 0;
683
684         /* Convert fractional part */
685         if (fracpart)
686         {
687                 do {
688                         temp = fracpart;
689                         my_modf(fracpart*0.1, &fracpart);
690                         temp = temp*0.1;
691                         index = (int) ((temp -fracpart +0.05)* 10.0);
692                         /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
693                         /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
694                         fconvert[fplace++] =
695                         (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
696                 } while(fracpart && (fplace < 311));
697                 if (fplace == 311) fplace--;
698         }
699         fconvert[fplace] = 0;
700
701         /* -1 for decimal point, another -1 if we are printing a sign */
702         padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
703         zpadlen = max - fplace;
704         if (zpadlen < 0) zpadlen = 0;
705         if (padlen < 0)
706                 padlen = 0;
707         if (flags & DP_F_MINUS)
708                 padlen = -padlen; /* Left Justifty */
709
710         if ((flags & DP_F_ZERO) && (padlen > 0)) {
711                 if (signvalue) {
712                         dopr_outch (buffer, currlen, maxlen, signvalue);
713                         --padlen;
714                         signvalue = 0;
715                 }
716                 while (padlen > 0) {
717                         dopr_outch (buffer, currlen, maxlen, '0');
718                         --padlen;
719                 }
720         }
721         while (padlen > 0) {
722                 dopr_outch (buffer, currlen, maxlen, ' ');
723                 --padlen;
724         }
725         if (signvalue)
726                 dopr_outch (buffer, currlen, maxlen, signvalue);
727
728         while (iplace > 0)
729                 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
730
731 #ifdef DEBUG_SNPRINTF
732         printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
733 #endif
734
735         /*
736          * Decimal point.  This should probably use locale to find the correct
737          * char to print out.
738          */
739         if (max > 0) {
740                 dopr_outch (buffer, currlen, maxlen, '.');
741
742                 while (fplace > 0)
743                         dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
744         }
745
746         while (zpadlen > 0) {
747                 dopr_outch (buffer, currlen, maxlen, '0');
748                 --zpadlen;
749         }
750
751         while (padlen < 0) {
752                 dopr_outch (buffer, currlen, maxlen, ' ');
753                 ++padlen;
754         }
755 }
756
757 static void
758 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)
767 int
768 vsnprintf (char *str, size_t count, const char *fmt, va_list args)
769 {
770         return dopr(str, count, fmt, args);
771 }
772 #endif
773
774 #if !defined(HAVE_SNPRINTF)
775 #ifdef PROTOTYPES
776 int
777 snprintf(char *str, size_t count, const char *fmt, ...)
778 #else
779 int
780 snprintf(str, count, fmt, va_alist)
781 char *str;
782 size_t count;
783 const char *fmt;
784 va_dcl
785 #endif
786 {
787         size_t ret;
788         va_list ap;
789
790         va_start(ap, fmt);
791         ret = vsnprintf(str, count, fmt, ap);
792         va_end(ap);
793         return ret;
794 }
795 #endif
796
797 #endif
798
799 /* -eof- */