1 /*
2 *
3 * Note: 25 May 2018. This code appears not to have a copyright, however,
4 * it appears to come from exactly the same code source as bsnprintf.c
5 * in the Bacula(R) - The Network Backup Solution
6
7 Copyright (C) 2000-2020 Kern Sibbald
8
9 The original author of Bacula is Kern Sibbald, with contributions
10 from many others, a complete list can be found in the file AUTHORS.
11
12 You may use this file and others of this release according to the
13 license defined in the LICENSE file, which includes the Affero General
14 Public License, v3.0 ("AGPLv3") and some additional permissions and
15 terms pursuant to its AGPLv3 Section 7.
16
17 This notice must be preserved when any source code is
18 conveyed and/or propagated.
19
20 Bacula(R) is a registered trademark of Kern Sibbald.
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 Bacula -- 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 /**************************************************************
37 * Original:
38 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
39 * A bombproof version of doprnt(dopr) included.
40 * Sigh. This sort of thing is always nasty do deal with. Note that
41 * the version here does not include floating point...
42 *
43 * snprintf() is used instead of sprintf() as it does limit checks
44 * for string length. This covers a nasty loophole.
45 *
46 * The other functions are there to prevent NULL pointers from
47 * causing nast effects.
48 *
49 * More Recently:
50 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
51 * This was ugly. It is still ugly. I opted out of floating point
52 * numbers, but the formatter understands just about everything
53 * from the normal C string format, at least as far as I can tell from
54 * the Solaris 2.5 printf(3S) man page.
55 *
56 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
57 * Ok, added some minimal floating point support, which means this
58 * probably requires libm on most operating systems. Don't yet
59 * support the exponent(e, E) and sigfig(g, G). Also, fmtint()
60 * was pretty badly broken, it just wasn't being exercised in ways
61 * which showed it, so that's been fixed. Also, formated the code
62 * to mutt conventions, and removed dead code left over from the
63 * original. Also, there is now a builtin-test, just compile with:
64 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
65 * and run snprintf for results.
66 *
67 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
68 * The PGP code was using unsigned hexadecimal formats.
69 * Unfortunately, unsigned formats simply didn't work.
70 *
71 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
72 * The original code assumed that both snprintf() and vsnprintf() were
73 * missing. Some systems only have snprintf() but not vsnprintf(), so
74 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
75 *
76 * Ben Lindstrom <mouring@eviladmin.org> 09/27/00 for OpenSSH
77 * Welcome to the world of %lld and %qd support. With other
78 * long long support. This is needed for sftp-server to work
79 * right.
80 *
81 * Ben Lindstrom <mouring@eviladmin.org> 02/12/01 for OpenSSH
82 * Removed all hint of VARARGS stuff and banished it to the void,
83 * and did a bit of KNF style work to make things a bit more
84 * acceptable. Consider stealing from mutt or enlightenment.
85 **************************************************************/
86
87 #include "bacula.h"
88 #include "compat.h"
89
90 typedef void (prfun)(char *, size_t *, size_t, int);
91
92 int
93 dopr(char *buffer, size_t maxlen, const char *format, va_list args, prfun);
94
95
96 static void
97 fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags,
98 int min, int max, prfun);
99
100 static void
101 fmtint(char *buffer, size_t *currlen, size_t maxlen, INT64 value, int base,
102 int min, int max, int flags, prfun);
103
104 static void
105 fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
106 int min, int max, int flags, prfun);
107
108 static void
109 dopr_outch(char *buffer, size_t *currlen, size_t maxlen, int c);
110
111 /*
112 * dopr(): poor man's version of doprintf
113 */
114
115 #ifndef MAX
116 #define MAX(a,b) ((a)>(b)?(a):(b))
117 #endif
118
119 /* format read states */
120 #define DP_S_DEFAULT 0
121 #define DP_S_FLAGS 1
122 #define DP_S_MIN 2
123 #define DP_S_DOT 3
124 #define DP_S_MAX 4
125 #define DP_S_MOD 5
126 #define DP_S_CONV 6
127 #define DP_S_DONE 7
128
129 /* format flags - Bits */
130 #define DP_F_MINUS (1 << 0)
131 #define DP_F_PLUS (1 << 1)
132 #define DP_F_SPACE (1 << 2)
133 #define DP_F_NUM (1 << 3)
134 #define DP_F_ZERO (1 << 4)
135 #define DP_F_UP (1 << 5)
136 #define DP_F_UNSIGNED (1 << 6)
137
138 /* Conversion Flags */
139 #define DP_C_SHORT 1
140 #define DP_C_LONG 2
141 #define DP_C_LDOUBLE 3
142 #define DP_C_LONG_LONG 4
143
144 #define char_to_int(p) (p - '0')
145 #define abs_val(p) (p < 0 ? -p : p)
146
147 static const char digitval[] = "0123456789abcdef0123456789ABCDEF";
148
149 int
dopr(char * buffer,size_t maxlen,const char * format,va_list args,prfun outch)150 dopr(char *buffer, size_t maxlen, const char *format, va_list args, prfun outch)
151 {
152 char *strvalue;
153 char ch;
154 INT64 value;
155 long double fvalue;
156 int min = 0;
157 int max = -1;
158 int state = DP_S_DEFAULT;
159 int flags = 0;
160 int cflags = 0;
161 size_t currlen = 0;
162
163 ch = *format++;
164
165 while (state != DP_S_DONE)
166 {
167 if ((ch == '\0') || (currlen >= maxlen))
168 state = DP_S_DONE;
169
170 switch (state)
171 {
172 case DP_S_DEFAULT:
173 if (ch == '%')
174 state = DP_S_FLAGS;
175 else
176 outch(buffer, &currlen, maxlen, ch);
177 ch = *format++;
178 break;
179 case DP_S_FLAGS:
180 switch (ch)
181 {
182 case '-':
183 flags |= DP_F_MINUS;
184 ch = *format++;
185 break;
186 case '+':
187 flags |= DP_F_PLUS;
188 ch = *format++;
189 break;
190 case ' ':
191 flags |= DP_F_SPACE;
192 ch = *format++;
193 break;
194 case '#':
195 flags |= DP_F_NUM;
196 ch = *format++;
197 break;
198 case '0':
199 flags |= DP_F_ZERO;
200 ch = *format++;
201 break;
202 default:
203 state = DP_S_MIN;
204 break;
205 }
206 break;
207 case DP_S_MIN:
208 if (isdigit((unsigned char)ch))
209 {
210 min = 10*min + char_to_int(ch);
211 ch = *format++;
212 }
213 else if (ch == '*')
214 {
215 min = va_arg(args, int);
216 ch = *format++;
217 state = DP_S_DOT;
218 }
219 else
220 state = DP_S_DOT;
221 break;
222 case DP_S_DOT:
223 if (ch == '.')
224 {
225 state = DP_S_MAX;
226 ch = *format++;
227 }
228 else
229 state = DP_S_MOD;
230 break;
231 case DP_S_MAX:
232 if (isdigit((unsigned char)ch))
233 {
234 if (max < 0)
235 max = 0;
236 max = 10*max + char_to_int(ch);
237 ch = *format++;
238 }
239 else if (ch == '*')
240 {
241 max = va_arg(args, int);
242 ch = *format++;
243 state = DP_S_MOD;
244 }
245 else
246 state = DP_S_MOD;
247 break;
248 case DP_S_MOD:
249 switch (ch)
250 {
251 case 'h':
252 cflags = DP_C_SHORT;
253 ch = *format++;
254 break;
255 case 'l':
256 cflags = DP_C_LONG;
257 ch = *format++;
258 if (ch == 'l')
259 {
260 cflags = DP_C_LONG_LONG;
261 ch = *format++;
262 }
263 break;
264 case 'q':
265 cflags = DP_C_LONG_LONG;
266 ch = *format++;
267 break;
268 case 'L':
269 cflags = DP_C_LDOUBLE;
270 ch = *format++;
271 break;
272 default:
273 break;
274 }
275 state = DP_S_CONV;
276 break;
277 case DP_S_CONV:
278 switch (ch)
279 {
280 case 'b':
281 flags |= DP_F_UNSIGNED;
282 if (cflags == DP_C_SHORT)
283 value = va_arg(args, unsigned int);
284 else if (cflags == DP_C_LONG)
285 value = va_arg(args, unsigned long int);
286 else if (cflags == DP_C_LONG_LONG)
287 value = va_arg(args, UINT64);
288 else
289 value = va_arg(args, unsigned int);
290 fmtint(buffer, &currlen, maxlen, value, 2, min, max, flags, outch);
291 break;
292 case 'd':
293 case 'i':
294 if (cflags == DP_C_SHORT)
295 value = va_arg(args, int);
296 else if (cflags == DP_C_LONG)
297 value = va_arg(args, long int);
298 else if (cflags == DP_C_LONG_LONG)
299 value = va_arg(args, INT64);
300 else
301 value = va_arg(args, int);
302 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags, outch);
303 break;
304 case 'o':
305 flags |= DP_F_UNSIGNED;
306 if (cflags == DP_C_SHORT)
307 value = va_arg(args, unsigned int);
308 else if (cflags == DP_C_LONG)
309 value = va_arg(args, unsigned long int);
310 else if (cflags == DP_C_LONG_LONG)
311 value = va_arg(args, UINT64);
312 else
313 value = va_arg(args, unsigned int);
314 fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags, outch);
315 break;
316 case 'u':
317 flags |= DP_F_UNSIGNED;
318 if (cflags == DP_C_SHORT)
319 value = va_arg(args, unsigned int);
320 else if (cflags == DP_C_LONG)
321 value = va_arg(args, unsigned long int);
322 else if (cflags == DP_C_LONG_LONG)
323 value = va_arg(args, UINT64);
324 else
325 value = va_arg(args, unsigned int);
326 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags, outch);
327 break;
328 case 'X':
329 flags |= DP_F_UP;
330 case 'x':
331 flags |= DP_F_UNSIGNED;
332 if (cflags == DP_C_SHORT)
333 value = va_arg(args, unsigned int);
334 else if (cflags == DP_C_LONG)
335 value = va_arg(args, unsigned long int);
336 else if (cflags == DP_C_LONG_LONG)
337 value = va_arg(args, UINT64);
338 else
339 value = va_arg(args, unsigned int);
340 fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags, outch);
341 break;
342 case 'f':
343 if (cflags == DP_C_LDOUBLE)
344 fvalue = va_arg(args, long double);
345 else
346 fvalue = va_arg(args, double);
347 /* um, floating point? */
348 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags, outch);
349 break;
350 case 'E':
351 flags |= DP_F_UP;
352 case 'e':
353 if (cflags == DP_C_LDOUBLE)
354 fvalue = va_arg(args, long double);
355 else
356 fvalue = va_arg(args, double);
357 break;
358 case 'G':
359 flags |= DP_F_UP;
360 case 'g':
361 if (cflags == DP_C_LDOUBLE)
362 fvalue = va_arg(args, long double);
363 else
364 fvalue = va_arg(args, double);
365 break;
366 case 'c':
367 outch(buffer, &currlen, maxlen, va_arg(args, int));
368 break;
369 case 's':
370 strvalue = va_arg(args, char *);
371 if (max < 0) {
372 max = maxlen; /* ie, no max */
373 }
374 fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max, outch);
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 fmtint(buffer, &currlen, maxlen, value, 16, min, max,
386 flags, outch);
387 break;
388 case 'n':
389 if (cflags == DP_C_SHORT)
390 {
391 short int *num;
392 num = va_arg(args, short int *);
393 *num = currlen;
394 }
395 else if (cflags == DP_C_LONG)
396 {
397 long int *num;
398 num = va_arg(args, long int *);
399 *num = currlen;
400 }
401 else if (cflags == DP_C_LONG_LONG)
402 {
403 INT64 *num;
404 num = va_arg(args, INT64 *);
405 *num = currlen;
406 }
407 else
408 {
409 int *num;
410 num = va_arg(args, int *);
411 *num = currlen;
412 }
413 break;
414 case '%':
415 outch(buffer, &currlen, maxlen, ch);
416 break;
417 case 'w': /* not supported yet, treat as next char */
418 ch = *format++;
419 break;
420 default: /* Unknown, skip */
421 break;
422 }
423 ch = *format++;
424 state = DP_S_DEFAULT;
425 flags = cflags = min = 0;
426 max = -1;
427 break;
428 case DP_S_DONE:
429 break;
430 default: /* hmm? */
431 break; /* some picky compilers need this */
432 }
433 }
434 outch(buffer, &currlen, maxlen, -1);
435 return currlen;
436 }
437
438 static void
fmtstr(char * buffer,size_t * currlen,size_t maxlen,char * value,int flags,int min,int max,prfun outch)439 fmtstr(char *buffer, size_t *currlen, size_t maxlen,
440 char *value, int flags, int min, int max, prfun outch)
441 {
442 int padlen, strln; /* amount to pad */
443 int cnt = 0;
444
445 if (value == NULL)
446 value = (char *)"<NULL>";
447
448 for (strln = 0; value[strln]; ++strln); /* strlen */
449 padlen = min - strln;
450 if (padlen < 0)
451 padlen = 0;
452 if (flags & DP_F_MINUS)
453 padlen = -padlen; /* Left Justify */
454
455 while ((padlen > 0) && (cnt < max)) {
456 outch(buffer, currlen, maxlen, ' ');
457 --padlen;
458 ++cnt;
459 }
460 while (*value && (cnt < max)) {
461 outch(buffer, currlen, maxlen, *value++);
462 ++cnt;
463 }
464 while ((padlen < 0) && (cnt < max)) {
465 outch(buffer, currlen, maxlen, ' ');
466 ++padlen;
467 ++cnt;
468 }
469 }
470
471 /* Have to handle DP_F_NUM(ie 0x and 0 alternates) */
472
473 static void
fmtint(char * buffer,size_t * currlen,size_t maxlen,INT64 value,int base,int min,int max,int flags,prfun outch)474 fmtint(char *buffer, size_t *currlen, size_t maxlen,
475 INT64 value, int base, int min, int max, int flags, prfun outch)
476 {
477 UINT64 uvalue;
478 char convert[20];
479 int signvalue = 0;
480 int place = 0;
481 int spadlen = 0; /* amount to space pad */
482 int zpadlen = 0; /* amount to zero pad */
483 int caps = 0;
484
485 if (max < 0)
486 max = 0;
487
488 uvalue = value;
489
490 if (!(flags & DP_F_UNSIGNED)) {
491 if (value < 0) {
492 signvalue = '-';
493 uvalue = -value;
494 }
495 else if (flags & DP_F_PLUS) /* Do a sign(+/i) */
496 signvalue = '+';
497 else if (flags & DP_F_SPACE)
498 signvalue = ' ';
499 }
500
501 if (flags & DP_F_UP)
502 caps = 16; /* Should characters be upper case? */
503
504 do {
505 convert[place++] = digitval[(uvalue%base)+caps];
506 uvalue = (uvalue / (unsigned)base);
507 } while (uvalue && (place < 20));
508
509 if (place == 20)
510 place--;
511
512 convert[place] = 0;
513
514 zpadlen = max - place;
515 spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
516
517 if (zpadlen < 0)
518 zpadlen = 0;
519 if (spadlen < 0)
520 spadlen = 0;
521 if (flags & DP_F_ZERO) {
522 zpadlen = MAX(zpadlen, spadlen);
523 spadlen = 0;
524 }
525 if (flags & DP_F_MINUS)
526 spadlen = -spadlen; /* Left Justifty */
527
528
529 /* Spaces */
530 while (spadlen > 0) {
531 outch(buffer, currlen, maxlen, ' ');
532 --spadlen;
533 }
534
535 /* Sign */
536 if (signvalue)
537 outch(buffer, currlen, maxlen, signvalue);
538
539 /* Zeros */
540 if (zpadlen > 0) {
541 while (zpadlen > 0) {
542 outch(buffer, currlen, maxlen, '0');
543 --zpadlen;
544 }
545 }
546
547 /* Digits */
548 while (place > 0)
549 outch(buffer, currlen, maxlen, convert[--place]);
550
551 /* Left Justified spaces */
552 while (spadlen < 0) {
553 outch(buffer, currlen, maxlen, ' ');
554 ++spadlen;
555 }
556 }
557
558 static long double
pow10(int exp)559 pow10(int exp)
560 {
561 long double result = 1;
562
563 while (exp)
564 {
565 result *= 10;
566 exp--;
567 }
568
569 return result;
570 }
571
572 static long
round(long double value)573 round(long double value)
574 {
575 long intpart = (long)value;
576
577 value -= intpart;
578 if (value >= 0.5)
579 intpart++;
580
581 return intpart;
582 }
583
584 static void
fmtfp(char * buffer,size_t * currlen,size_t maxlen,long double fvalue,int min,int max,int flags,prfun outch)585 fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
586 int min, int max, int flags, prfun outch)
587 {
588 char iconvert[20];
589 char fconvert[20];
590 int signvalue = 0;
591 int iplace = 0;
592 int fplace = 0;
593 int padlen = 0; /* amount to pad */
594 int zpadlen = 0;
595 long intpart;
596 long fracpart;
597 long double ufvalue;
598
599 /*
600 * AIX manpage says the default is 0, but Solaris says the default
601 * is 6, and sprintf on AIX defaults to 6
602 */
603 if (max < 0)
604 max = 6;
605
606 ufvalue = abs_val(fvalue);
607
608 if (fvalue < 0)
609 signvalue = '-';
610 else if (flags & DP_F_PLUS) /* Do a sign(+/i) */
611 signvalue = '+';
612 else if (flags & DP_F_SPACE)
613 signvalue = ' ';
614
615 intpart = (long)ufvalue;
616
617 /*
618 * Sorry, we only support 9 digits past the decimal because of our
619 * conversion method
620 */
621 if (max > 9)
622 max = 9;
623
624 /* We "cheat" by converting the fractional part to integer by
625 * multiplying by a factor of 10
626 */
627 fracpart = round((pow10 (max)) * (ufvalue - intpart));
628
629 if (fracpart >= pow10 (max))
630 {
631 intpart++;
632 fracpart -= (long)pow10 (max);
633 }
634
635 /* Convert integer part */
636 do
637 {
638 iconvert[iplace++] = digitval[intpart % 10];
639 intpart = (intpart / 10);
640 } while (intpart && (iplace < 20));
641
642 if (iplace == 20)
643 iplace--;
644
645 iconvert[iplace] = 0;
646
647 /* Convert fractional part */
648 do
649 {
650 fconvert[fplace++] = digitval[fracpart % 10];
651 fracpart = (fracpart / 10);
652 } while (fracpart && (fplace < 20));
653
654 if (fplace == 20)
655 fplace--;
656
657 fconvert[fplace] = 0;
658
659 /* -1 for decimal point, another -1 if we are printing a sign */
660 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
661 zpadlen = max - fplace;
662 if (zpadlen < 0)
663 zpadlen = 0;
664 if (padlen < 0)
665 padlen = 0;
666 if (flags & DP_F_MINUS)
667 padlen = -padlen; /* Left Justifty */
668
669 if ((flags & DP_F_ZERO) && (padlen > 0))
670 {
671 if (signvalue)
672 {
673 outch(buffer, currlen, maxlen, signvalue);
674 --padlen;
675 signvalue = 0;
676 }
677 while (padlen > 0)
678 {
679 outch(buffer, currlen, maxlen, '0');
680 --padlen;
681 }
682 }
683
684 while (padlen > 0)
685 {
686 outch(buffer, currlen, maxlen, ' ');
687 --padlen;
688 }
689
690 if (signvalue)
691 outch(buffer, currlen, maxlen, signvalue);
692
693 while (iplace > 0)
694 outch(buffer, currlen, maxlen, iconvert[--iplace]);
695
696 /*
697 * Decimal point. This should probably use locale to find the correct
698 * char to print out.
699 */
700 outch(buffer, currlen, maxlen, '.');
701
702 while (fplace > 0)
703 outch(buffer, currlen, maxlen, fconvert[--fplace]);
704
705 while (zpadlen > 0)
706 {
707 outch(buffer, currlen, maxlen, '0');
708 --zpadlen;
709 }
710
711 while (padlen < 0)
712 {
713 outch(buffer, currlen, maxlen, ' ');
714 ++padlen;
715 }
716 }
717
718 static void
dopr_outch(char * buffer,size_t * currlen,size_t maxlen,int c)719 dopr_outch(char *buffer, size_t *currlen, size_t maxlen, int c)
720 {
721 if (c == -1)
722 {
723 if (*currlen < maxlen - 1)
724 buffer[*currlen] = '\0';
725 else
726 buffer[maxlen - 1] = '\0';
727 }
728 else if (*currlen < maxlen)
729 buffer[(*currlen)++] = c;
730 }
731
732 int
__sprintf(char * str,const char * fmt,...)733 __sprintf(char *str, const char *fmt, ...)
734 {
735 int rval;
736 va_list ap;
737 va_start(ap, fmt);
738 rval = vsnprintf(str, 128*1024, fmt, ap);
739 va_end(ap);
740 return rval;
741 }
742
743
744 int
__snprintf(char * str,size_t count,const char * fmt,...)745 __snprintf(char *str, size_t count, const char *fmt, ...)
746 {
747 int rval;
748 va_list ap;
749
750 va_start(ap, fmt);
751 rval = vsnprintf(str, count, fmt, ap);
752 va_end(ap);
753
754 return rval;
755 }
756
757 int
__vsprintf(char * s,const char * format,va_list args)758 __vsprintf(char *s, const char *format, va_list args)
759 {
760 s[0] = 0;
761 return dopr(s, 0xfffff, format, args, dopr_outch);
762 }
763
764 int
__vsnprintf(char * s,size_t count,const char * format,va_list args)765 __vsnprintf(char *s, size_t count, const char *format, va_list args)
766 {
767 s[0] = 0;
768 return dopr(s, count, format, args, dopr_outch);
769 }
770