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