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