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