blob: 85e9ebf98a095ca4dd61ec2b187e5b675bef1d8f [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file fmt/print.c Formatted printing
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_sa.h>
9#include <re_fmt.h>
10#include <re_mem.h>
11#include <math.h>
12#ifdef _MSC_VER
13#include <float.h>
14#ifndef isinf
15#define isinf(d) (!_finite(d))
16#endif
17#ifndef isnan
18#define isnan(d) _isnan(d)
19#endif
20#endif
21#ifdef SOLARIS
22#include <ieeefp.h>
23#undef isinf
24#define isinf(a) (fpclass((a)) == FP_NINF || fpclass((a)) == FP_PINF)
25#undef isnan
26#define isnan(a) isnand((a))
27#endif
28
29
30enum length_modifier {
31 LENMOD_NONE = 0,
32 LENMOD_LONG = 1,
33 LENMOD_LONG_LONG = 2,
34 LENMOD_SIZE = 42,
35};
36
37enum {
38 DEC_SIZE = 42,
39 NUM_SIZE = 64
40};
41
42static const char prfx_neg[] = "-";
43static const char prfx_hex[] = "0x";
44static const char str_nil[] = "(nil)";
45
46
47static int write_padded(const char *p, size_t sz, size_t pad, char pch,
48 bool plr, const char *prfx, re_vprintf_h *vph,
49 void *arg)
50{
51 const size_t prfx_len = str_len(prfx);
52 int err = 0;
53
54 pad -= MIN(pad, prfx_len);
55
56 if (prfx && pch == '0')
57 err |= vph(prfx, prfx_len, arg);
58
59 while (!plr && (pad-- > sz))
60 err |= vph(&pch, 1, arg);
61
62 if (prfx && pch != '0')
63 err |= vph(prfx, prfx_len, arg);
64
65 if (p && sz)
66 err |= vph(p, sz, arg);
67
68 while (plr && pad-- > sz)
69 err |= vph(&pch, 1, arg);
70
71 return err;
72}
73
74
75static uint32_t local_itoa(char *buf, uint64_t n, uint8_t base, bool uc)
76{
77 char c, *p = buf + NUM_SIZE;
78 uint32_t len = 1;
79 const char a = uc ? 'A' : 'a';
80
81 *--p = '\0';
82 do {
83 const uint64_t dv = n / base;
84 const uint64_t mul = dv * base;
85
86 c = (char)(n - mul);
87
88 if (c < 10)
89 *--p = '0' + c;
90 else
91 *--p = a + (c - 10);
92
93 n = dv;
94 ++len;
95
96 } while (n != 0);
97
98 memmove(buf, p, len);
99
100 return len - 1;
101}
102
103
104static size_t local_ftoa(char *buf, double n, size_t dp)
105{
106 char *p = buf;
107 long long a = (long long)n;
108 double b = n - (double)a;
109
110 b = (b < 0) ? -b : b;
111
112 /* integral part */
113 p += local_itoa(p, (a < 0) ? -a : a, 10, false);
114
115 *p++ = '.';
116
117 /* decimal digits */
118 while (dp--) {
119 char v;
120
121 b *= 10;
122 v = (char)b;
123 b -= v;
124
125 *p++ = '0' + (char)v;
126 }
127
128 *p = '\0';
129
130 return p - buf;
131}
132
133
134/**
135 * Print a formatted string
136 *
137 * @param fmt Formatted string
138 * @param ap Variable argument
139 * @param vph Print handler
140 * @param arg Handler argument
141 *
142 * @return 0 if success, otherwise errorcode
143 *
144 * Extensions:
145 *
146 * <pre>
147 * %b (char *, size_t) Buffer string with pointer and length
148 * %r (struct pl) Pointer-length object
149 * %w (uint8_t *, size_t) Binary buffer to hexadecimal format
150 * %j (struct sa *) Socket address - address part only
151 * %J (struct sa *) Socket address and port - like 1.2.3.4:1234
152 * %H (re_printf_h *, void *) Print handler with argument
153 * %v (char *fmt, va_list *) Variable argument list
154 * %m (int) Describe an error code
155 * </pre>
156 *
157 * Reserved for the future:
158 *
159 * %k
160 * %y
161 *
162 */
163int re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg)
164{
165 uint8_t base, *bptr;
166 char pch, ch, num[NUM_SIZE], addr[64], msg[256];
167 enum length_modifier lenmod = LENMOD_NONE;
168 struct re_printf pf;
169 bool fm = false, plr = false;
170 const struct pl *pl;
171 size_t pad = 0, fpad = -1, len, i;
172 const char *str, *p = fmt, *p0 = fmt;
173 const struct sa *sa;
174 re_printf_h *ph;
175 void *ph_arg;
176 va_list *apl;
177 int err = 0;
178 void *ptr;
179 uint64_t n;
180 int64_t sn;
181 bool uc = false;
182 double dbl;
183
184 if (!fmt || !vph)
185 return EINVAL;
186
187 pf.vph = vph;
188 pf.arg = arg;
189
190 for (;*p && !err; p++) {
191
192 if (!fm) {
193 if (*p != '%')
194 continue;
195
196 pch = ' ';
197 plr = false;
198 pad = 0;
199 fpad = -1;
200 lenmod = LENMOD_NONE;
201 uc = false;
202
203 if (p > p0)
204 err |= vph(p0, p - p0, arg);
205
206 fm = true;
207 continue;
208 }
209
210 fm = false;
211 base = 10;
212
213 switch (*p) {
214
215 case '-':
216 plr = true;
217 fm = true;
218 break;
219
220 case '.':
221 fpad = pad;
222 pad = 0;
223 fm = true;
224 break;
225
226 case '%':
227 ch = '%';
228
229 err |= vph(&ch, 1, arg);
230 break;
231
232 case 'b':
233 str = va_arg(ap, const char *);
234 len = va_arg(ap, size_t);
235
236 err |= write_padded(str, str ? len : 0, pad, ' ',
237 plr, NULL, vph, arg);
238 break;
239
240 case 'c':
241 ch = va_arg(ap, int);
242
243 err |= write_padded(&ch, 1, pad, ' ', plr, NULL,
244 vph, arg);
245 break;
246
247 case 'd':
248 case 'i':
249 switch (lenmod) {
250
251 case LENMOD_SIZE:
252 sn = va_arg(ap, ssize_t);
253 break;
254
255 default:
256 case LENMOD_LONG_LONG:
257 sn = va_arg(ap, signed long long);
258 break;
259
260 case LENMOD_LONG:
261 sn = va_arg(ap, signed long);
262 break;
263
264 case LENMOD_NONE:
265 sn = va_arg(ap, signed);
266 break;
267 }
268
269 len = local_itoa(num, (sn < 0) ? -sn : sn, base,
270 false);
271
272 err |= write_padded(num, len, pad,
273 plr ? ' ' : pch, plr,
274 (sn < 0) ? prfx_neg : NULL,
275 vph, arg);
276 break;
277
278 case 'f':
279 case 'F':
280 dbl = va_arg(ap, double);
281
282 if (fpad == (size_t)-1) {
283 fpad = pad;
284 pad = 0;
285 }
286
287 if (isinf(dbl)) {
288 err |= write_padded("inf", 3, fpad,
289 ' ', plr, NULL, vph, arg);
290 }
291 else if (isnan(dbl)) {
292 err |= write_padded("nan", 3, fpad,
293 ' ', plr, NULL, vph, arg);
294 }
295 else {
296 len = local_ftoa(num, dbl,
297 pad ? min(pad, DEC_SIZE) : 6);
298
299 err |= write_padded(num, len, fpad,
300 plr ? ' ' : pch, plr,
301 (dbl<0) ? prfx_neg : NULL,
302 vph, arg);
303 }
304 break;
305
306 case 'H':
307 ph = va_arg(ap, re_printf_h *);
308 ph_arg = va_arg(ap, void *);
309
310 if (ph)
311 err |= ph(&pf, ph_arg);
312 break;
313
314 case 'l':
315 ++lenmod;
316 fm = true;
317 break;
318
319 case 'm':
320 str = str_error(va_arg(ap, int), msg, sizeof(msg));
321 err |= write_padded(str, str_len(str), pad,
322 ' ', plr, NULL, vph, arg);
323 break;
324
325 case 'p':
326 ptr = va_arg(ap, void *);
327
328 if (ptr) {
329 len = local_itoa(num, (unsigned long int)ptr,
330 16, false);
331 err |= write_padded(num, len, pad,
332 plr ? ' ' : pch, plr,
333 prfx_hex, vph, arg);
334 }
335 else {
336 err |= write_padded(str_nil,
337 sizeof(str_nil) - 1,
338 pad, ' ', plr, NULL,
339 vph, arg);
340 }
341 break;
342
343 case 'r':
344 pl = va_arg(ap, const struct pl *);
345
346 err |= write_padded(pl ? pl->p : NULL,
347 (pl && pl->p) ? pl->l : 0,
348 pad, ' ', plr, NULL, vph, arg);
349 break;
350
351 case 's':
352 str = va_arg(ap, const char *);
353 err |= write_padded(str, str_len(str), pad,
354 ' ', plr, NULL, vph, arg);
355 break;
356
357 case 'X':
358 uc = true;
359 /*@fallthrough@*/
360 case 'x':
361 base = 16;
362 /*@fallthrough@*/
363 case 'u':
364 switch (lenmod) {
365
366 case LENMOD_SIZE:
367 n = va_arg(ap, size_t);
368 break;
369
370 default:
371 case LENMOD_LONG_LONG:
372 n = va_arg(ap, unsigned long long);
373 break;
374
375 case LENMOD_LONG:
376 n = va_arg(ap, unsigned long);
377 break;
378
379 case LENMOD_NONE:
380 n = va_arg(ap, unsigned);
381 break;
382 }
383
384 len = local_itoa(num, n, base, uc);
385
386 err |= write_padded(num, len, pad,
387 plr ? ' ' : pch, plr, NULL,
388 vph, arg);
389 break;
390
391 case 'v':
392 str = va_arg(ap, char *);
393 apl = va_arg(ap, va_list *);
394
395 if (!str || !apl)
396 break;
397
398 err |= re_vhprintf(str, *apl, vph, arg);
399 break;
400
401 case 'W':
402 uc = true;
403 /*@fallthrough@*/
404 case 'w':
405 bptr = va_arg(ap, uint8_t *);
406 len = va_arg(ap, size_t);
407
408 len = bptr ? len : 0;
409 pch = plr ? ' ' : pch;
410
411 while (!plr && pad-- > (len * 2))
412 err |= vph(&pch, 1, arg);
413
414 for (i=0; i<len; i++) {
415 const uint8_t v = *bptr++;
416 uint32_t l = local_itoa(num, v, 16, uc);
417 err |= write_padded(num, l, 2, '0',
418 false, NULL, vph, arg);
419 }
420
421 while (plr && pad-- > (len * 2))
422 err |= vph(&pch, 1, arg);
423
424 break;
425
426 case 'z':
427 lenmod = LENMOD_SIZE;
428 fm = true;
429 break;
430
431 case 'j':
432 sa = va_arg(ap, struct sa *);
433 if (!sa)
434 break;
435 if (sa_ntop(sa, addr, sizeof(addr))) {
436 err |= write_padded("?", 1, pad, ' ',
437 plr, NULL, vph, arg);
438 break;
439 }
440 err |= write_padded(addr, strlen(addr), pad, ' ',
441 plr, NULL, vph, arg);
442 break;
443
444
445 case 'J':
446 sa = va_arg(ap, struct sa *);
447 if (!sa)
448 break;
449 if (sa_ntop(sa, addr, sizeof(addr))) {
450 err |= write_padded("?", 1, pad, ' ',
451 plr, NULL, vph, arg);
452 break;
453 }
454
455#ifdef HAVE_INET6
456 if (AF_INET6 == sa_af(sa)) {
457 ch = '[';
458 err |= vph(&ch, 1, arg);
459 }
460#endif
461 err |= write_padded(addr, strlen(addr), pad, ' ',
462 plr, NULL, vph, arg);
463#ifdef HAVE_INET6
464 if (AF_INET6 == sa_af(sa)) {
465 ch = ']';
466 err |= vph(&ch, 1, arg);
467 }
468#endif
469
470 ch = ':';
471 err |= vph(&ch, 1, arg);
472 len = local_itoa(num, sa_port(sa), 10, false);
473 err |= write_padded(num, len, pad,
474 plr ? ' ' : pch, plr, NULL,
475 vph, arg);
476
477 break;
478
479 default:
480 if (('0' <= *p) && (*p <= '9')) {
481 if (!pad && ('0' == *p)) {
482 pch = '0';
483 }
484 else {
485 pad *= 10;
486 pad += *p - '0';
487 }
488 fm = true;
489 break;
490 }
491
492 ch = '?';
493
494 err |= vph(&ch, 1, arg);
495 break;
496 }
497
498 if (!fm)
499 p0 = p + 1;
500 }
501
502 if (!fm && p > p0)
503 err |= vph(p0, p - p0, arg);
504
505 return err;
506}
507
508
509static int print_handler(const char *p, size_t size, void *arg)
510{
511 struct pl *pl = arg;
512
513 if (size > pl->l)
514 return ENOMEM;
515
516 memcpy((void *)pl->p, p, size);
517
518 pl_advance(pl, size);
519
520 return 0;
521}
522
523
524struct dyn_print {
525 char *str;
526 char *p;
527 size_t l;
528 size_t size;
529};
530
531
532static int print_handler_dyn(const char *p, size_t size, void *arg)
533{
534 struct dyn_print *dp = arg;
535
536 if (size > dp->l - 1) {
537 const size_t new_size = MAX(dp->size + size, dp->size * 2);
538 char *str = mem_realloc(dp->str, new_size);
539 if (!str)
540 return ENOMEM;
541
542 dp->str = str;
543 dp->l += new_size - dp->size;
544 dp->p = dp->str + new_size - dp->l;
545 dp->size = new_size;
546 }
547
548 memcpy(dp->p, p, size);
549
550 dp->p += size;
551 dp->l -= size;
552
553 return 0;
554}
555
556
557struct strm_print {
558 FILE *f;
559 size_t n;
560};
561
562static int print_handler_stream(const char *p, size_t size, void *arg)
563{
564 struct strm_print *sp = arg;
565
566 if (1 != fwrite(p, size, 1, sp->f))
567 return ENOMEM;
568
569 sp->n += size;
570
571 return 0;
572}
573
574
575/**
576 * Print a formatted string to a file stream, using va_list
577 *
578 * @param stream File stream for the output
579 * @param fmt Formatted string
580 * @param ap Variable-arguments list
581 *
582 * @return The number of characters printed, or -1 if error
583 */
584int re_vfprintf(FILE *stream, const char *fmt, va_list ap)
585{
586 struct strm_print sp;
587
588 if (!stream)
589 return -1;
590
591 sp.f = stream;
592 sp.n = 0;
593
594 if (0 != re_vhprintf(fmt, ap, print_handler_stream, &sp))
595 return -1;
596
597 return (int)sp.n;
598}
599
600
601/**
602 * Print a formatted string to stdout, using va_list
603 *
604 * @param fmt Formatted string
605 * @param ap Variable-arguments list
606 *
607 * @return The number of characters printed, or -1 if error
608 */
609int re_vprintf(const char *fmt, va_list ap)
610{
611 return re_vfprintf(stdout, fmt, ap);
612}
613
614
615/**
616 * Print a formatted string to a buffer, using va_list
617 *
618 * @param str Buffer for output string
619 * @param size Size of buffer
620 * @param fmt Formatted string
621 * @param ap Variable-arguments list
622 *
623 * @return The number of characters printed, or -1 if error
624 */
625int re_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
626{
627 struct pl pl;
628 int err;
629
630 if (!str || !size)
631 return -1;
632
633 pl.p = str;
634 pl.l = size - 1;
635
636 err = re_vhprintf(fmt, ap, print_handler, &pl);
637
638 str[size - pl.l - 1] = '\0';
639
640 return err ? -1 : (int)(size - pl.l - 1);
641}
642
643
644/**
645 * Print a formatted string to a dynamically allocated buffer, using va_list
646 *
647 * @param strp Pointer for output string
648 * @param fmt Formatted string
649 * @param ap Variable-arguments list
650 *
651 * @return 0 if success, otherwise errorcode
652 */
653int re_vsdprintf(char **strp, const char *fmt, va_list ap)
654{
655 struct dyn_print dp;
656 int err;
657
658 if (!strp)
659 return EINVAL;
660
661 dp.size = 16;
662 dp.str = mem_alloc(dp.size, NULL);
663 if (!dp.str)
664 return ENOMEM;
665
666 dp.p = dp.str;
667 dp.l = dp.size;
668
669 err = re_vhprintf(fmt, ap, print_handler_dyn, &dp);
670 if (err)
671 goto out;
672
673 *dp.p = '\0';
674
675 out:
676 if (err)
677 mem_deref(dp.str);
678 else
679 *strp = dp.str;
680
681 return err;
682}
683
684
685/**
686 * Print a formatted string
687 *
688 * @param pf Print backend
689 * @param fmt Formatted string
690 *
691 * @return 0 if success, otherwise errorcode
692 */
693int re_hprintf(struct re_printf *pf, const char *fmt, ...)
694{
695 va_list ap;
696 int err;
697
698 if (!pf)
699 return EINVAL;
700
701 va_start(ap, fmt);
702 err = re_vhprintf(fmt, ap, pf->vph, pf->arg);
703 va_end(ap);
704
705 return err;
706}
707
708
709/**
710 * Print a formatted string to a file stream
711 *
712 * @param stream File stream for output
713 * @param fmt Formatted string
714 *
715 * @return The number of characters printed, or -1 if error
716 */
717int re_fprintf(FILE *stream, const char *fmt, ...)
718{
719 va_list ap;
720 int n;
721
722 va_start(ap, fmt);
723 n = re_vfprintf(stream, fmt, ap);
724 va_end(ap);
725
726 return n;
727}
728
729
730/**
731 * Print a formatted string to stdout
732 *
733 * @param fmt Formatted string
734 *
735 * @return The number of characters printed, or -1 if error
736 */
737int re_printf(const char *fmt, ...)
738{
739 va_list ap;
740 int n;
741
742 va_start(ap, fmt);
743 n = re_vprintf(fmt, ap);
744 va_end(ap);
745
746 return n;
747}
748
749
750/**
751 * Print a formatted string to a buffer
752 *
753 * @param str Buffer for output string
754 * @param size Size of buffer
755 * @param fmt Formatted string
756 *
757 * @return The number of characters printed, or -1 if error
758 */
759int re_snprintf(char *str, size_t size, const char *fmt, ...)
760{
761 va_list ap;
762 int n;
763
764 va_start(ap, fmt);
765 n = re_vsnprintf(str, size, fmt, ap);
766 va_end(ap);
767
768 return n;
769}
770
771
772/**
773 * Print a formatted string to a buffer
774 *
775 * @param strp Buffer pointer for output string
776 * @param fmt Formatted string
777 *
778 * @return 0 if success, otherwise errorcode
779 */
780int re_sdprintf(char **strp, const char *fmt, ...)
781{
782 va_list ap;
783 int err;
784
785 va_start(ap, fmt);
786 err = re_vsdprintf(strp, fmt, ap);
787 va_end(ap);
788
789 return err;
790}