1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2005-2012 Free Software Foundation Europe e.V.
5 
6    This program is Free Software; you can redistribute it and/or
7    modify it under the terms of version three of the GNU Affero General Public
8    License as published by the Free Software Foundation and included
9    in the file LICENSE.
10 
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14    Affero General Public License for more details.
15 
16    You should have received a copy of the GNU Affero General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301, USA.
20 */
21 /*
22  * Copyright Patrick Powell 1995
23  *
24  * This code is based on code written by Patrick Powell
25  * (papowell@astart.com) It may be used for any purpose as long
26  * as this notice remains intact on all source code distributions.
27  *
28  * Adapted for BAREOS -- note there were lots of bugs in
29  * the original code: %lld and %s were seriously broken, and
30  * with FP turned off %f seg faulted.
31  *
32  * Kern Sibbald, November MMV
33  */
34 
35 #include "include/bareos.h"
36 #include <wchar.h>
37 
38 #define FP_OUTPUT 1 /* BAREOS uses floating point */
39 
40 /* Define the following if you want all the features of
41  *  normal printf, but with all the security problems.
42  *  For BAREOS we turn this off, and it silently ignores
43  *  formats that could pose a security problem.
44  */
45 #undef SECURITY_PROBLEM
46 
47 #ifdef HAVE_LONG_DOUBLE
48 #define LDOUBLE long double
49 #else
50 #define LDOUBLE double
51 #endif
52 
53 int Bvsnprintf(char* buffer, int32_t maxlen, const char* format, va_list args);
54 static int32_t fmtstr(char* buffer,
55                       int32_t currlen,
56                       int32_t maxlen,
57                       const char* value,
58                       int flags,
59                       int min,
60                       int max);
61 static int32_t fmtwstr(char* buffer,
62                        int32_t currlen,
63                        int32_t maxlen,
64                        const wchar_t* value,
65                        int flags,
66                        int min,
67                        int max);
68 static int32_t fmtint(char* buffer,
69                       int32_t currlen,
70                       int32_t maxlen,
71                       int64_t value,
72                       int base,
73                       int min,
74                       int max,
75                       int flags);
76 
77 #ifdef FP_OUTPUT
78 #ifdef HAVE_FCVTL
79 #define fcvt fcvtl
80 #endif
81 static int32_t fmtfp(char* buffer,
82                      int32_t currlen,
83                      int32_t maxlen,
84                      LDOUBLE fvalue,
85                      int min,
86                      int max,
87                      int flags);
88 #else
89 #define fmtfp(b, c, m, f, min, max, fl) currlen
90 #endif
91 
92 /*
93  *  NOTE!!!! do not use this #define with a construct such
94  *    as outch(--place);.  It just will NOT work, because the
95  *    decrement of place is done ONLY if there is room in the
96  *    output buffer.
97  */
98 #define outch(c)            \
99   {                         \
100     int len = currlen;      \
101     if (currlen < maxlen) { \
102       buffer[len] = (c);    \
103       currlen++;            \
104     }                       \
105   }
106 
107 /* format read states */
108 #define DP_S_DEFAULT 0
109 #define DP_S_FLAGS 1
110 #define DP_S_MIN 2
111 #define DP_S_DOT 3
112 #define DP_S_MAX 4
113 #define DP_S_MOD 5
114 #define DP_S_CONV 6
115 #define DP_S_DONE 7
116 
117 /* format flags - Bits */
118 #define DP_F_MINUS (1 << 0)
119 #define DP_F_PLUS (1 << 1)
120 #define DP_F_SPACE (1 << 2)
121 #define DP_F_NUM (1 << 3)
122 #define DP_F_ZERO (1 << 4)
123 #define DP_F_UP (1 << 5)
124 #define DP_F_UNSIGNED (1 << 6)
125 #define DP_F_DOT (1 << 7)
126 
127 /* Conversion Flags */
128 #define DP_C_INT16 1
129 #define DP_C_INT32 2
130 #define DP_C_LDOUBLE 3
131 #define DP_C_INT64 4
132 #define DP_C_WCHAR 5 /* wide characters */
133 #define DP_C_SIZE_T 6
134 
135 #define char_to_int(p) ((p) - '0')
136 #undef MAX
137 #define MAX(p, q) (((p) >= (q)) ? (p) : (q))
138 
139 /*
140   You might ask why does BAREOS have it's own printf routine? Well,
141   There are two reasons: 1. Here (as opposed to library routines), we
142   define %d and %ld to be 32 bit; %lld and %q to be 64 bit.  2. We
143   disable %n for security reasons.
144  */
145 
Bsnprintf(char * str,int32_t size,const char * fmt,...)146 int Bsnprintf(char* str, int32_t size, const char* fmt, ...)
147 {
148   va_list arg_ptr;
149   int len;
150 
151   va_start(arg_ptr, fmt);
152   len = Bvsnprintf(str, size, fmt, arg_ptr);
153   va_end(arg_ptr);
154   return len;
155 }
156 
157 
Bvsnprintf(char * buffer,int32_t maxlen,const char * format,va_list args)158 int Bvsnprintf(char* buffer, int32_t maxlen, const char* format, va_list args)
159 {
160   char ch;
161   int64_t value;
162   char* strvalue;
163   wchar_t* wstrvalue;
164   int min;
165   int max;
166   int state;
167   int flags;
168   int cflags;
169   int32_t currlen;
170   int base;
171 #ifdef FP_OUTPUT
172   LDOUBLE fvalue;
173 #endif
174 
175   state = DP_S_DEFAULT;
176   currlen = flags = cflags = min = 0;
177   max = -1;
178   ch = *format++;
179   *buffer = 0;
180 
181   while (state != DP_S_DONE) {
182     if ((ch == '\0') || (currlen >= maxlen)) { state = DP_S_DONE; }
183     switch (state) {
184       case DP_S_DEFAULT:
185         if (ch == '%') {
186           state = DP_S_FLAGS;
187         } else {
188           outch(ch);
189         }
190         ch = *format++;
191         break;
192       case DP_S_FLAGS:
193         switch (ch) {
194           case '-':
195             flags |= DP_F_MINUS;
196             ch = *format++;
197             break;
198           case '+':
199             flags |= DP_F_PLUS;
200             ch = *format++;
201             break;
202           case ' ':
203             flags |= DP_F_SPACE;
204             ch = *format++;
205             break;
206           case '#':
207             flags |= DP_F_NUM;
208             ch = *format++;
209             break;
210           case '0':
211             flags |= DP_F_ZERO;
212             ch = *format++;
213             break;
214           default:
215             state = DP_S_MIN;
216             break;
217         }
218         break;
219       case DP_S_MIN:
220         if (isdigit((unsigned char)ch)) {
221           min = 10 * min + char_to_int(ch);
222           ch = *format++;
223         } else if (ch == '*') {
224           min = va_arg(args, int);
225           ch = *format++;
226           state = DP_S_DOT;
227         } else
228           state = DP_S_DOT;
229         break;
230       case DP_S_DOT:
231         if (ch == '.') {
232           state = DP_S_MAX;
233           flags |= DP_F_DOT;
234           ch = *format++;
235         } else
236           state = DP_S_MOD;
237         break;
238       case DP_S_MAX:
239         if (isdigit((unsigned char)ch)) {
240           if (max < 0) 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         break;
250       case DP_S_MOD:
251         switch (ch) {
252           case 'h':
253             cflags = DP_C_INT16;
254             ch = *format++;
255             break;
256           case 'l':
257             cflags = DP_C_INT32;
258             ch = *format++;
259             if (ch == 's') {
260               cflags = DP_C_WCHAR;
261             } else if (ch == 'l') { /* It's a long long */
262               cflags = DP_C_INT64;
263               ch = *format++;
264             }
265             break;
266           case 'z':
267             cflags = DP_C_SIZE_T;
268             ch = *format++;
269             break;
270           case 'L':
271             cflags = DP_C_LDOUBLE;
272             ch = *format++;
273             break;
274           case 'q': /* same as long long */
275             cflags = DP_C_INT64;
276             ch = *format++;
277             break;
278           default:
279             break;
280         }
281         state = DP_S_CONV;
282         break;
283       case DP_S_CONV:
284         switch (ch) {
285           case 'd':
286           case 'i':
287             if (cflags == DP_C_INT16) {
288               value = va_arg(args, int32_t);
289             } else if (cflags == DP_C_INT32) {
290               value = va_arg(args, int32_t);
291             } else if (cflags == DP_C_INT64) {
292               value = va_arg(args, int64_t);
293             } else if (cflags == DP_C_SIZE_T) {
294               value = va_arg(args, ssize_t);
295             } else {
296               value = va_arg(args, int);
297             }
298             currlen =
299                 fmtint(buffer, currlen, maxlen, value, 10, min, max, flags);
300             break;
301           case 'X':
302           case 'x':
303           case 'o':
304           case 'u':
305             if (ch == 'o') {
306               base = 8;
307             } else if (ch == 'x') {
308               base = 16;
309             } else if (ch == 'X') {
310               base = 16;
311               flags |= DP_F_UP;
312             } else {
313               base = 10;
314             }
315             flags |= DP_F_UNSIGNED;
316             if (cflags == DP_C_INT16) {
317               value = va_arg(args, uint32_t);
318             } else if (cflags == DP_C_INT32) {
319               value = va_arg(args, uint32_t);
320             } else if (cflags == DP_C_INT64) {
321               value = va_arg(args, uint64_t);
322             } else if (cflags == DP_C_SIZE_T) {
323               value = va_arg(args, size_t);
324             } else {
325               value = va_arg(args, unsigned int);
326             }
327             currlen =
328                 fmtint(buffer, currlen, maxlen, value, base, min, max, flags);
329             break;
330           case 'f':
331             if (cflags == DP_C_LDOUBLE) {
332               fvalue = va_arg(args, LDOUBLE);
333             } else {
334               fvalue = va_arg(args, double);
335             }
336             currlen = fmtfp(buffer, currlen, maxlen, fvalue, min, max, flags);
337             break;
338           case 'E':
339             flags |= DP_F_UP;
340           case 'e':
341             if (cflags == DP_C_LDOUBLE) {
342               fvalue = va_arg(args, LDOUBLE);
343             } else {
344               fvalue = va_arg(args, double);
345             }
346             currlen = fmtfp(buffer, currlen, maxlen, fvalue, min, max, flags);
347             break;
348           case 'G':
349             flags |= DP_F_UP;
350           case 'g':
351             if (cflags == DP_C_LDOUBLE) {
352               fvalue = va_arg(args, LDOUBLE);
353             } else {
354               fvalue = va_arg(args, double);
355             }
356             currlen = fmtfp(buffer, currlen, maxlen, fvalue, min, max, flags);
357             break;
358           case 'c':
359             ch = va_arg(args, int);
360             outch(ch);
361             break;
362           case 's':
363             if (cflags != DP_C_WCHAR) {
364               strvalue = va_arg(args, char*);
365               if (!strvalue) { strvalue = (char*)"<NULL>"; }
366               currlen =
367                   fmtstr(buffer, currlen, maxlen, strvalue, flags, min, max);
368             } else {
369               /* %ls means to edit wide characters */
370               wstrvalue = va_arg(args, wchar_t*);
371               if (!wstrvalue) { wstrvalue = (wchar_t*)L"<NULL>"; }
372               currlen =
373                   fmtwstr(buffer, currlen, maxlen, wstrvalue, flags, min, max);
374             }
375             break;
376           case 'p':
377             flags |= DP_F_UNSIGNED;
378             if (sizeof(char*) == 4) {
379               value = va_arg(args, uint32_t);
380             } else if (sizeof(char*) == 8) {
381               value = va_arg(args, uint64_t);
382             } else {
383               value = 0; /* we have a problem */
384             }
385             currlen =
386                 fmtint(buffer, currlen, maxlen, value, 16, min, max, flags);
387             break;
388 
389 #ifdef SECURITY_PROBLEM
390           case 'n':
391             if (cflags == DP_C_INT16) {
392               int16_t* num;
393               num = va_arg(args, int16_t*);
394               *num = currlen;
395             } else if (cflags == DP_C_INT32) {
396               int32_t* num;
397               num = va_arg(args, int32_t*);
398               *num = (int32_t)currlen;
399             } else if (cflags == DP_C_INT64) {
400               int64_t* num;
401               num = va_arg(args, int64_t*);
402               *num = (int64_t)currlen;
403             } else {
404               int32_t* num;
405               num = va_arg(args, int32_t*);
406               *num = (int32_t)currlen;
407             }
408             break;
409 #endif
410           case '%':
411             outch(ch);
412             break;
413           case 'w':
414             /* not supported yet, skip char */
415             format++;
416             break;
417           default:
418             /* Unknown, skip */
419             break;
420         }
421         ch = *format++;
422         state = DP_S_DEFAULT;
423         flags = cflags = min = 0;
424         max = -1;
425         break;
426       case DP_S_DONE:
427         break;
428       default:
429         /* hmm? */
430         break; /* some picky compilers need this */
431     }
432   }
433   if (currlen < maxlen - 1) {
434     buffer[currlen] = '\0';
435   } else {
436     buffer[maxlen - 1] = '\0';
437   }
438   return currlen;
439 }
440 
fmtstr(char * buffer,int32_t currlen,int32_t maxlen,const char * value,int flags,int min,int max)441 static int32_t fmtstr(char* buffer,
442                       int32_t currlen,
443                       int32_t maxlen,
444                       const char* value,
445                       int flags,
446                       int min,
447                       int max)
448 {
449   int padlen, strln; /* amount to pad */
450   int cnt = 0;
451   char ch;
452 
453 
454   if (flags & DP_F_DOT && max < 0) { /* Max not specified */
455     max = 0;
456   } else if (max < 0) {
457     max = maxlen;
458   }
459   strln = strlen(value);
460   if (strln > max) { strln = max; /* truncate to max */ }
461   padlen = min - strln;
462   if (padlen < 0) { padlen = 0; }
463   if (flags & DP_F_MINUS) { padlen = -padlen; /* Left Justify */ }
464 
465   while (padlen > 0) {
466     outch(' ');
467     --padlen;
468   }
469   while (*value && (cnt < max)) {
470     ch = *value++;
471     outch(ch);
472     ++cnt;
473   }
474   while (padlen < 0) {
475     outch(' ');
476     ++padlen;
477   }
478   return currlen;
479 }
480 
fmtwstr(char * buffer,int32_t currlen,int32_t maxlen,const wchar_t * value,int flags,int min,int max)481 static int32_t fmtwstr(char* buffer,
482                        int32_t currlen,
483                        int32_t maxlen,
484                        const wchar_t* value,
485                        int flags,
486                        int min,
487                        int max)
488 {
489   int padlen, strln; /* amount to pad */
490   int cnt = 0;
491   char ch;
492 
493 
494   if (flags & DP_F_DOT && max < 0) { /* Max not specified */
495     max = 0;
496   } else if (max < 0) {
497     max = maxlen;
498   }
499   strln = wcslen(value);
500   if (strln > max) { strln = max; /* truncate to max */ }
501   padlen = min - strln;
502   if (padlen < 0) { padlen = 0; }
503   if (flags & DP_F_MINUS) { padlen = -padlen; /* Left Justify */ }
504 
505   while (padlen > 0) {
506     outch(' ');
507     --padlen;
508   }
509   while (*value && (cnt < max)) {
510     ch = (*value++) & 0xff;
511     outch(ch);
512     ++cnt;
513   }
514   while (padlen < 0) {
515     outch(' ');
516     ++padlen;
517   }
518   return currlen;
519 }
520 
521 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
522 
fmtint(char * buffer,int32_t currlen,int32_t maxlen,int64_t value,int base,int min,int max,int flags)523 static int32_t fmtint(char* buffer,
524                       int32_t currlen,
525                       int32_t maxlen,
526                       int64_t value,
527                       int base,
528                       int min,
529                       int max,
530                       int flags)
531 {
532   int signvalue = 0;
533   uint64_t uvalue;
534   char convert[25];
535   int place = 0;
536   int spadlen = 0; /* amount to space pad */
537   int zpadlen = 0; /* amount to zero pad */
538   int caps = 0;
539   const char* cvt_string;
540 
541   if (max < 0) { max = 0; }
542 
543   uvalue = value;
544 
545   if (!(flags & DP_F_UNSIGNED)) {
546     if (value < 0) {
547       signvalue = '-';
548       uvalue = -value;
549     } else if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
550       signvalue = '+';
551     } else if (flags & DP_F_SPACE) {
552       signvalue = ' ';
553     }
554   }
555 
556   if (flags & DP_F_UP) { caps = 1; /* Should characters be upper case? */ }
557 
558   cvt_string = caps ? "0123456789ABCDEF" : "0123456789abcdef";
559   do {
560     convert[place++] = cvt_string[uvalue % (unsigned)base];
561     uvalue = (uvalue / (unsigned)base);
562   } while (uvalue && (place < (int)sizeof(convert)));
563   if (place == (int)sizeof(convert)) { place--; }
564   convert[place] = 0;
565 
566   zpadlen = max - place;
567   spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
568   if (zpadlen < 0) zpadlen = 0;
569   if (spadlen < 0) spadlen = 0;
570   if (flags & DP_F_ZERO) {
571     zpadlen = MAX(zpadlen, spadlen);
572     spadlen = 0;
573   }
574   if (flags & DP_F_MINUS) spadlen = -spadlen; /* Left Justifty */
575 
576 #ifdef DEBUG_SNPRINTF
577   printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", zpadlen, spadlen,
578          min, max, place);
579 #endif
580 
581   /* Spaces */
582   while (spadlen > 0) {
583     outch(' ');
584     --spadlen;
585   }
586 
587   /* Sign */
588   if (signvalue) { outch(signvalue); }
589 
590   /* Zeros */
591   if (zpadlen > 0) {
592     while (zpadlen > 0) {
593       outch('0');
594       --zpadlen;
595     }
596   }
597 
598   /* Output digits backward giving correct order */
599   while (place > 0) {
600     place--;
601     outch(convert[place]);
602   }
603 
604   /* Left Justified spaces */
605   while (spadlen < 0) {
606     outch(' ');
607     ++spadlen;
608   }
609   return currlen;
610 }
611 
612 #ifdef FP_OUTPUT
613 
abs_val(LDOUBLE value)614 static LDOUBLE abs_val(LDOUBLE value)
615 {
616   LDOUBLE result = value;
617 
618   if (value < 0) result = -value;
619 
620   return result;
621 }
622 
pow10(int exp)623 static LDOUBLE pow10(int exp)
624 {
625   LDOUBLE result = 1;
626 
627   while (exp) {
628     result *= 10;
629     exp--;
630   }
631 
632   return result;
633 }
634 
roundtoint(LDOUBLE value)635 static int64_t roundtoint(LDOUBLE value)
636 {
637   int64_t intpart;
638 
639   intpart = (int64_t)value;
640   value = value - intpart;
641   if (value >= 0.5) intpart++;
642 
643   return intpart;
644 }
645 
fmtfp(char * buffer,int32_t currlen,int32_t maxlen,LDOUBLE fvalue,int min,int max,int flags)646 static int32_t fmtfp(char* buffer,
647                      int32_t currlen,
648                      int32_t maxlen,
649                      LDOUBLE fvalue,
650                      int min,
651                      int max,
652                      int flags)
653 {
654   int signvalue = 0;
655   LDOUBLE ufvalue;
656 #ifndef HAVE_FCVT
657   char iconvert[311];
658   char fconvert[311];
659 #else
660   char iconvert[311];
661   char fconvert[311];
662   char* result;
663   char dummy[10];
664   int dec_pt, sig;
665   int r_length;
666   extern char* fcvt(double value, int ndigit, int* decpt, int* sign);
667 #endif
668   int fiter;
669   int iplace = 0;
670   int fplace = 0;
671   int padlen = 0; /* amount to pad */
672   int zpadlen = 0;
673   int64_t intpart;
674   int64_t fracpart;
675   const char* cvt_str;
676 
677   /*
678    * AIX manpage says the default is 0, but Solaris says the default
679    * is 6, and sprintf on AIX defaults to 6
680    */
681   if (max < 0) max = 6;
682 
683   ufvalue = abs_val(fvalue);
684 
685   if (fvalue < 0)
686     signvalue = '-';
687   else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
688     signvalue = '+';
689   else if (flags & DP_F_SPACE)
690     signvalue = ' ';
691 
692 #ifndef HAVE_FCVT
693   intpart = (int64_t)ufvalue;
694 
695   /*
696    * Sorry, we only support 9 digits past the decimal because of our
697    * conversion method
698    */
699   if (max > 9) max = 9;
700 
701   /* We "cheat" by converting the fractional part to integer by
702    * multiplying by a factor of 10
703    */
704   fracpart = roundtoint((pow10(max)) * (ufvalue - intpart));
705 
706   if (fracpart >= pow10(max)) {
707     intpart++;
708     fracpart -= (int64_t)pow10(max);
709   }
710 
711 #ifdef DEBUG_SNPRINTF
712   printf("fmtfp: %g %lld.%lld min=%d max=%d\n", (double)fvalue, intpart,
713          fracpart, min, max);
714 #endif
715 
716   /* Convert integer part */
717   cvt_str = "0123456789";
718   do {
719     iconvert[iplace++] = cvt_str[(int)(intpart % 10)];
720     intpart = (intpart / 10);
721   } while (intpart && (iplace < (int)sizeof(iconvert)));
722 
723   if (iplace == (int)sizeof(fconvert)) { iplace--; }
724   iconvert[iplace] = 0;
725 
726   /* Convert fractional part */
727   cvt_str = "0123456789";
728   fiter = max;
729   do {
730     fconvert[fplace++] = cvt_str[fracpart % 10];
731     fracpart = (fracpart / 10);
732   } while (--fiter);
733 
734   if (fplace == (int)sizeof(fconvert)) { fplace--; }
735   fconvert[fplace] = 0;
736 #else /* use fcvt() */
737   if (max > 310) { max = 310; }
738 #ifdef HAVE_FCVTL
739   result = fcvtl(ufvalue, max, &dec_pt, &sig);
740 #else
741   result = fcvt(ufvalue, max, &dec_pt, &sig);
742 #endif
743 
744   if (!result) {
745     r_length = 0;
746     dummy[0] = 0;
747     result = dummy;
748   } else {
749     r_length = strlen(result);
750   }
751 
752   /*
753    * Fix broken fcvt implementation returns..
754    */
755 
756   if (r_length == 0) {
757     result[0] = '0';
758     result[1] = '\0';
759     r_length = 1;
760   }
761 
762   if (r_length < dec_pt) dec_pt = r_length;
763 
764   if (dec_pt <= 0) {
765     iplace = 1;
766     iconvert[0] = '0';
767     iconvert[1] = '\0';
768 
769     fplace = 0;
770 
771     while (r_length) { fconvert[fplace++] = result[--r_length]; }
772 
773     while ((dec_pt < 0) && (fplace < max)) {
774       fconvert[fplace++] = '0';
775       dec_pt++;
776     }
777   } else {
778     int c;
779 
780     iplace = 0;
781     for (c = dec_pt; c; iconvert[iplace++] = result[--c])
782       ;
783     iconvert[iplace] = '\0';
784 
785     result += dec_pt;
786     fplace = 0;
787 
788     for (c = (r_length - dec_pt); c; fconvert[fplace++] = result[--c])
789       ;
790   }
791 #endif /* HAVE_FCVT */
792 
793   /* -1 for decimal point, another -1 if we are printing a sign */
794   padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
795   zpadlen = max - fplace;
796   if (zpadlen < 0) { zpadlen = 0; }
797   if (padlen < 0) { padlen = 0; }
798   if (flags & DP_F_MINUS) { padlen = -padlen; /* Left Justifty */ }
799 
800   if ((flags & DP_F_ZERO) && (padlen > 0)) {
801     if (signvalue) {
802       outch(signvalue);
803       --padlen;
804       signvalue = 0;
805     }
806     while (padlen > 0) {
807       outch('0');
808       --padlen;
809     }
810   }
811   while (padlen > 0) {
812     outch(' ');
813     --padlen;
814   }
815   if (signvalue) { outch(signvalue); }
816 
817   while (iplace > 0) {
818     iplace--;
819     outch(iconvert[iplace]);
820   }
821 
822 
823 #ifdef DEBUG_SNPRINTF
824   printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
825 #endif
826 
827   /*
828    * Decimal point.  This should probably use locale to find the correct
829    * char to print out.
830    */
831   if (max > 0) {
832     outch('.');
833     while (fplace > 0) {
834       fplace--;
835       outch(fconvert[fplace]);
836     }
837   }
838 
839   while (zpadlen > 0) {
840     outch('0');
841     --zpadlen;
842   }
843 
844   while (padlen < 0) {
845     outch(' ');
846     ++padlen;
847   }
848   return currlen;
849 }
850 #endif /* FP_OUTPUT */
851