c11 safe string handling support
[vpp.git] / src / vppinfra / format.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*------------------------------------------------------------------
16  * format.c -- see notice below
17  *
18  * October 2003, Eliot Dresselhaus
19  *
20  * Modifications to this file Copyright (c) 2003 by cisco Systems, Inc.
21  * All rights reserved.
22  *------------------------------------------------------------------
23  */
24
25 /*
26   Copyright (c) 2001, 2002, 2003, 2006 Eliot Dresselhaus
27
28   Permission is hereby granted, free of charge, to any person obtaining
29   a copy of this software and associated documentation files (the
30   "Software"), to deal in the Software without restriction, including
31   without limitation the rights to use, copy, modify, merge, publish,
32   distribute, sublicense, and/or sell copies of the Software, and to
33   permit persons to whom the Software is furnished to do so, subject to
34   the following conditions:
35
36   The above copyright notice and this permission notice shall be
37   included in all copies or substantial portions of the Software.
38
39   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46 */
47
48 #include <stdarg.h>             /* va_start, etc */
49
50 #ifdef CLIB_UNIX
51 #include <unistd.h>
52 #include <stdio.h>
53 #endif
54
55 #ifdef CLIB_STANDALONE
56 #include <vppinfra/standalone_stdio.h>
57 #endif
58
59 #include <vppinfra/mem.h>
60 #include <vppinfra/format.h>
61 #include <vppinfra/vec.h>
62 #include <vppinfra/error.h>
63 #include <vppinfra/string.h>
64 #include <vppinfra/os.h>        /* os_puts */
65 #include <vppinfra/math.h>
66
67 typedef struct
68 {
69   /* Output number in this base. */
70   u8 base;
71
72   /* Number of show of 64 bit number. */
73   u8 n_bits;
74
75   /* Signed or unsigned. */
76   u8 is_signed;
77
78   /* Output digits uppercase (not lowercase) %X versus %x. */
79   u8 uppercase_digits;
80 } format_integer_options_t;
81
82 static u8 *format_integer (u8 * s, u64 number,
83                            format_integer_options_t * options);
84 static u8 *format_float (u8 * s, f64 x, uword n_digits_to_print,
85                          uword output_style);
86
87 typedef struct
88 {
89   /* String justification: + => right, - => left, = => center. */
90   uword justify;
91
92   /* Width of string (before and after decimal point for numbers).
93      0 => natural width. */
94   uword width[2];
95
96   /* Long => 'l', long long 'L', int 0. */
97   uword how_long;
98
99   /* Pad character.  Defaults to space. */
100   uword pad_char;
101 } format_info_t;
102
103 static u8 *
104 justify (u8 * s, format_info_t * fi, uword s_len_orig)
105 {
106   uword i0, l0, l1;
107
108   i0 = s_len_orig;
109   l0 = i0 + fi->width[0];
110   l1 = vec_len (s);
111
112   /* If width is zero user returned width. */
113   if (l0 == i0)
114     l0 = l1;
115
116   if (l1 > l0)
117     _vec_len (s) = l0;
118   else if (l0 > l1)
119     {
120       uword n = l0 - l1;
121       uword n_left = 0, n_right = 0;
122
123       switch (fi->justify)
124         {
125         case '-':
126           n_right = n;
127           break;
128
129         case '+':
130           n_left = n;
131           break;
132
133         case '=':
134           n_right = n_left = n / 2;
135           if (n % 2)
136             n_left++;
137           break;
138         }
139       if (n_left > 0)
140         {
141           vec_insert (s, n_left, i0);
142           clib_memset (s + i0, fi->pad_char, n_left);
143           l1 = vec_len (s);
144         }
145       if (n_right > 0)
146         {
147           vec_resize (s, n_right);
148           clib_memset (s + l1, fi->pad_char, n_right);
149         }
150     }
151   return s;
152 }
153
154 static const u8 *
155 do_percent (u8 ** _s, const u8 * fmt, va_list * va)
156 {
157   u8 *s = *_s;
158   uword c;
159
160   const u8 *f = fmt;
161
162   format_info_t fi = {
163     .justify = '+',
164     .width = {0},
165     .pad_char = ' ',
166     .how_long = 0,
167   };
168
169   uword i;
170
171   ASSERT (f[0] == '%');
172
173   switch (c = *++f)
174     {
175     case '%':
176       /* %% => % */
177       vec_add1 (s, c);
178       f++;
179       goto done;
180
181     case '-':
182     case '+':
183     case '=':
184       fi.justify = c;
185       c = *++f;
186       break;
187     }
188
189   /* Parse width0 . width1. */
190   {
191     uword is_first_digit = 1;
192
193     fi.width[0] = fi.width[1] = 0;
194     for (i = 0; i < 2; i++)
195       {
196         if (c == '0' && i == 0 && is_first_digit)
197           fi.pad_char = '0';
198         is_first_digit = 0;
199         if (c == '*')
200           {
201             fi.width[i] = va_arg (*va, int);
202             c = *++f;
203           }
204         else
205           {
206             while (c >= '0' && c <= '9')
207               {
208                 fi.width[i] = 10 * fi.width[i] + (c - '0');
209                 c = *++f;
210               }
211           }
212         if (c != '.')
213           break;
214         c = *++f;
215       }
216   }
217
218   /* Parse %l* and %L* */
219   switch (c)
220     {
221     case 'w':
222       /* word format. */
223       fi.how_long = 'w';
224       c = *++f;
225       break;
226
227     case 'L':
228     case 'l':
229       fi.how_long = c;
230       c = *++f;
231       if (c == 'l' && *f == 'l')
232         {
233           fi.how_long = 'L';
234           c = *++f;
235         }
236       break;
237     }
238
239   /* Finally we are ready for format letter. */
240   if (c != 0)
241     {
242       uword s_initial_len = vec_len (s);
243       format_integer_options_t o = {
244         .is_signed = 0,
245         .base = 10,
246         .n_bits = BITS (uword),
247         .uppercase_digits = 0,
248       };
249
250       f++;
251
252       switch (c)
253         {
254         default:
255           {
256             /* Try to give a helpful error message. */
257             vec_free (s);
258             s = format (s, "**** CLIB unknown format `%%%c' ****", c);
259             goto done;
260           }
261
262         case 'c':
263           vec_add1 (s, va_arg (*va, int));
264           break;
265
266         case 'p':
267           vec_add1 (s, '0');
268           vec_add1 (s, 'x');
269
270           o.is_signed = 0;
271           o.n_bits = BITS (uword *);
272           o.base = 16;
273           o.uppercase_digits = 0;
274
275           s = format_integer (s, pointer_to_uword (va_arg (*va, void *)), &o);
276           break;
277
278         case 'x':
279         case 'X':
280         case 'u':
281         case 'd':
282           {
283             u64 number;
284
285             o.base = 10;
286             if (c == 'x' || c == 'X')
287               o.base = 16;
288             o.is_signed = c == 'd';
289             o.uppercase_digits = c == 'X';
290
291             switch (fi.how_long)
292               {
293               case 'L':
294                 number = va_arg (*va, unsigned long long);
295                 o.n_bits = BITS (unsigned long long);
296                 break;
297
298               case 'l':
299                 number = va_arg (*va, long);
300                 o.n_bits = BITS (long);
301                 break;
302
303               case 'w':
304                 number = va_arg (*va, word);
305                 o.n_bits = BITS (uword);
306                 break;
307
308               default:
309                 number = va_arg (*va, int);
310                 o.n_bits = BITS (int);
311                 break;
312               }
313
314             s = format_integer (s, number, &o);
315           }
316           break;
317
318         case 's':
319         case 'S':
320           {
321             char *cstring = va_arg (*va, char *);
322             uword len;
323
324             if (!cstring)
325               {
326                 cstring = "(nil)";
327                 len = 5;
328               }
329             else if (fi.width[1] != 0)
330               len = clib_min (strlen (cstring), fi.width[1]);
331             else
332               len = strlen (cstring);
333
334             /* %S => format string as C identifier (replace _ with space). */
335             if (c == 'S')
336               {
337                 for (i = 0; i < len; i++)
338                   vec_add1 (s, cstring[i] == '_' ? ' ' : cstring[i]);
339               }
340             else
341               vec_add (s, cstring, len);
342           }
343           break;
344
345         case 'v':
346           {
347             u8 *v = va_arg (*va, u8 *);
348             uword len;
349
350             if (fi.width[1] != 0)
351               len = clib_min (vec_len (v), fi.width[1]);
352             else
353               len = vec_len (v);
354
355             vec_add (s, v, len);
356           }
357           break;
358
359         case 'f':
360         case 'g':
361         case 'e':
362           /* Floating point. */
363           ASSERT (fi.how_long == 0 || fi.how_long == 'l');
364           s = format_float (s, va_arg (*va, double), fi.width[1], c);
365           break;
366
367         case 'U':
368           /* User defined function. */
369           {
370             typedef u8 *(user_func_t) (u8 * s, va_list * args);
371             user_func_t *u = va_arg (*va, user_func_t *);
372
373             s = (*u) (s, va);
374           }
375           break;
376         }
377
378       s = justify (s, &fi, s_initial_len);
379     }
380
381 done:
382   *_s = s;
383   return f;
384 }
385
386 u8 *
387 va_format (u8 * s, const char *fmt, va_list * va)
388 {
389   const u8 *f = (u8 *) fmt, *g;
390   u8 c;
391
392   g = f;
393   while (1)
394     {
395       c = *f;
396
397       if (!c)
398         break;
399
400       if (c == '%')
401         {
402           if (f > g)
403             vec_add (s, g, f - g);
404           f = g = do_percent (&s, f, va);
405         }
406       else
407         {
408           f++;
409         }
410     }
411
412   if (f > g)
413     vec_add (s, g, f - g);
414
415   return s;
416 }
417
418 u8 *
419 format (u8 * s, const char *fmt, ...)
420 {
421   va_list va;
422   va_start (va, fmt);
423   s = va_format (s, fmt, &va);
424   va_end (va);
425   return s;
426 }
427
428 word
429 va_fformat (FILE * f, char *fmt, va_list * va)
430 {
431   word ret;
432   u8 *s;
433
434   s = va_format (0, fmt, va);
435
436 #ifdef CLIB_UNIX
437   if (f)
438     {
439       ret = fwrite (s, vec_len (s), 1, f);
440     }
441   else
442 #endif /* CLIB_UNIX */
443     {
444       ret = 0;
445       os_puts (s, vec_len (s), /* is_error */ 0);
446     }
447
448   vec_free (s);
449   return ret;
450 }
451
452 word
453 fformat (FILE * f, char *fmt, ...)
454 {
455   va_list va;
456   word ret;
457
458   va_start (va, fmt);
459   ret = va_fformat (f, fmt, &va);
460   va_end (va);
461
462   return (ret);
463 }
464
465 #ifdef CLIB_UNIX
466 void
467 fformat_append_cr (FILE * ofp, const char *fmt, ...)
468 {
469   va_list va;
470
471   va_start (va, fmt);
472   (void) va_fformat (ofp, (char *) fmt, &va);
473   va_end (va);
474   fformat (ofp, "\n");
475 }
476
477 word
478 fdformat (int fd, char *fmt, ...)
479 {
480   word ret;
481   u8 *s;
482   va_list va;
483
484   va_start (va, fmt);
485   s = va_format (0, fmt, &va);
486   va_end (va);
487
488   ret = write (fd, s, vec_len (s));
489   vec_free (s);
490   return ret;
491 }
492 #endif
493
494 /* Format integral type. */
495 static u8 *
496 format_integer (u8 * s, u64 number, format_integer_options_t * options)
497 {
498   u64 q;
499   u32 r;
500   u8 digit_buffer[128];
501   u8 *d = digit_buffer + sizeof (digit_buffer);
502   word c, base;
503
504   if (options->is_signed && (i64) number < 0)
505     {
506       number = -number;
507       vec_add1 (s, '-');
508     }
509
510   if (options->n_bits < BITS (number))
511     number &= ((u64) 1 << options->n_bits) - 1;
512
513   base = options->base;
514
515   while (1)
516     {
517       q = number / base;
518       r = number % base;
519
520       if (r < 10 + 26 + 26)
521         {
522           if (r < 10)
523             c = '0' + r;
524           else if (r < 10 + 26)
525             c = 'a' + (r - 10);
526           else
527             c = 'A' + (r - 10 - 26);
528
529           if (options->uppercase_digits
530               && base <= 10 + 26 && c >= 'a' && c <= 'z')
531             c += 'A' - 'a';
532
533           *--d = c;
534         }
535       else                      /* will never happen, warning be gone */
536         {
537           *--d = '?';
538         }
539
540       if (q == 0)
541         break;
542
543       number = q;
544     }
545
546   vec_add (s, d, digit_buffer + sizeof (digit_buffer) - d);
547   return s;
548 }
549
550 /* Floating point formatting. */
551 /* Deconstruct IEEE 64 bit number into sign exponent and fraction. */
552 #define f64_down(f,sign,expon,fraction)                         \
553 do {                                                            \
554   union { u64 u; f64 f; } _f64_down_tmp;                        \
555   _f64_down_tmp.f = (f);                                        \
556   (sign) = (_f64_down_tmp.u >> 63);                             \
557   (expon) = ((_f64_down_tmp.u >> 52) & 0x7ff) - 1023;           \
558   (fraction) = ((_f64_down_tmp.u << 12) >> 12) | ((u64) 1 << 52); \
559 } while (0)
560
561 /* Construct IEEE 64 bit number. */
562 static f64
563 f64_up (uword sign, word expon, u64 fraction)
564 {
565   union
566   {
567     u64 u;
568     f64 f;
569   } tmp;
570
571   tmp.u = (u64) ((sign) != 0) << 63;
572
573   expon += 1023;
574   if (expon > 1023)
575     expon = 1023;
576   if (expon < 0)
577     expon = 0;
578   tmp.u |= (u64) expon << 52;
579
580   tmp.u |= fraction & (((u64) 1 << 52) - 1);
581
582   return tmp.f;
583 }
584
585 /* Returns approximate precision of number given its exponent. */
586 static f64
587 f64_precision (int base2_expon)
588 {
589   static int n_bits = 0;
590
591   if (!n_bits)
592     {
593       /* Compute number of significant bits in floating point representation. */
594       f64 one = 0;
595       f64 small = 1;
596
597       while (one != 1)
598         {
599           small *= .5;
600           n_bits++;
601           one = 1 + small;
602         }
603     }
604
605   return f64_up (0, base2_expon - n_bits, 0);
606 }
607
608 /* Return x 10^n */
609 static f64
610 times_power_of_ten (f64 x, int n)
611 {
612   if (n >= 0)
613     {
614       static f64 t[8] = { 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, };
615       while (n >= 8)
616         {
617           x *= 1e+8;
618           n -= 8;
619         }
620       return x * t[n];
621     }
622   else
623     {
624       static f64 t[8] = { 1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, };
625       while (n <= -8)
626         {
627           x *= 1e-8;
628           n += 8;
629         }
630       return x * t[-n];
631     }
632
633 }
634
635 /* Write x = y * 10^expon with 1 < y < 10. */
636 static f64
637 normalize (f64 x, word * expon_return, f64 * prec_return)
638 {
639   word expon2, expon10;
640   CLIB_UNUSED (u64 fraction);
641   CLIB_UNUSED (word sign);
642   f64 prec;
643
644   f64_down (x, sign, expon2, fraction);
645
646   expon10 =
647     .5 +
648     expon2 * .301029995663981195213738894724493 /* Log (2) / Log (10) */ ;
649
650   prec = f64_precision (expon2);
651   x = times_power_of_ten (x, -expon10);
652   prec = times_power_of_ten (prec, -expon10);
653
654   while (x < 1)
655     {
656       x *= 10;
657       prec *= 10;
658       expon10--;
659     }
660
661   while (x > 10)
662     {
663       x *= .1;
664       prec *= .1;
665       expon10++;
666     }
667
668   if (x + prec >= 10)
669     {
670       x = 1;
671       expon10++;
672     }
673
674   *expon_return = expon10;
675   *prec_return = prec;
676
677   return x;
678 }
679
680 static u8 *
681 add_some_zeros (u8 * s, uword n_zeros)
682 {
683   while (n_zeros > 0)
684     {
685       vec_add1 (s, '0');
686       n_zeros--;
687     }
688   return s;
689 }
690
691 /* Format a floating point number with the given number of fractional
692    digits (e.g. 1.2345 with 2 fraction digits yields "1.23") and output style. */
693 static u8 *
694 format_float (u8 * s, f64 x, uword n_fraction_digits, uword output_style)
695 {
696   f64 prec;
697   word sign, expon, n_fraction_done, added_decimal_point;
698   /* Position of decimal point relative to where we are. */
699   word decimal_point;
700
701   /* Default number of digits to print when its not specified. */
702   if (n_fraction_digits == ~0)
703     n_fraction_digits = 7;
704   n_fraction_done = 0;
705   decimal_point = 0;
706   added_decimal_point = 0;
707   sign = expon = 0;
708
709   /* Special case: zero. */
710   if (x == 0)
711     {
712     do_zero:
713       vec_add1 (s, '0');
714       goto done;
715     }
716
717   if (x < 0)
718     {
719       x = -x;
720       sign = 1;
721     }
722
723   /* Check for not-a-number. */
724   if (isnan (x))
725     return format (s, "%cNaN", sign ? '-' : '+');
726
727   /* Check for infinity. */
728   if (isinf (x))
729     return format (s, "%cinfinity", sign ? '-' : '+');
730
731   x = normalize (x, &expon, &prec);
732
733   /* Not enough digits to print anything: so just print 0 */
734   if ((word) - expon > (word) n_fraction_digits
735       && (output_style == 'f' || (output_style == 'g')))
736     goto do_zero;
737
738   if (sign)
739     vec_add1 (s, '-');
740
741   if (output_style == 'f'
742       || (output_style == 'g' && expon > -10 && expon < 10))
743     {
744       if (expon < 0)
745         {
746           /* Add decimal point and leading zeros. */
747           vec_add1 (s, '.');
748           n_fraction_done = clib_min (-(expon + 1), n_fraction_digits);
749           s = add_some_zeros (s, n_fraction_done);
750           decimal_point = -n_fraction_done;
751           added_decimal_point = 1;
752         }
753       else
754         decimal_point = expon + 1;
755     }
756   else
757     {
758       /* Exponential output style. */
759       decimal_point = 1;
760       output_style = 'e';
761     }
762
763   while (1)
764     {
765       uword digit;
766
767       /* Number is smaller than precision: call it zero. */
768       if (x < prec)
769         break;
770
771       digit = x;
772       x -= digit;
773       if (x + prec >= 1)
774         {
775           digit++;
776           x -= 1;
777         }
778
779       /* Round last printed digit. */
780       if (decimal_point <= 0
781           && n_fraction_done + 1 == n_fraction_digits && digit < 9)
782         digit += x >= .5;
783
784       vec_add1 (s, '0' + digit);
785
786       /* Move rightwards towards/away from decimal point. */
787       decimal_point--;
788
789       n_fraction_done += decimal_point < 0;
790       if (decimal_point <= 0 && n_fraction_done >= n_fraction_digits)
791         break;
792
793       if (decimal_point == 0 && x != 0)
794         {
795           vec_add1 (s, '.');
796           added_decimal_point = 1;
797         }
798
799       x *= 10;
800       prec *= 10;
801     }
802
803 done:
804   if (decimal_point > 0)
805     {
806       s = add_some_zeros (s, decimal_point);
807       decimal_point = 0;
808     }
809
810   if (n_fraction_done < n_fraction_digits)
811     {
812       if (!added_decimal_point)
813         vec_add1 (s, '.');
814       s = add_some_zeros (s, n_fraction_digits - n_fraction_done);
815     }
816
817   if (output_style == 'e')
818     s = format (s, "e%wd", expon);
819
820   return s;
821 }
822
823
824 /*
825  * fd.io coding-style-patch-verification: ON
826  *
827  * Local Variables:
828  * eval: (c-set-style "gnu")
829  * End:
830  */