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