blob: c0247b495e28f7d874e22a8f58884766a1f66d3c [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
Austin Schuh35a2f492021-04-07 21:41:56 -0700306 case 'h':
307 lenmod = LENMOD_NONE;
308 fm = true;
309 break;
310
James Kuszmaul82f6c042021-01-17 11:30:16 -0800311 case 'H':
312 ph = va_arg(ap, re_printf_h *);
313 ph_arg = va_arg(ap, void *);
314
315 if (ph)
316 err |= ph(&pf, ph_arg);
317 break;
318
319 case 'l':
320 ++lenmod;
321 fm = true;
322 break;
323
324 case 'm':
325 str = str_error(va_arg(ap, int), msg, sizeof(msg));
326 err |= write_padded(str, str_len(str), pad,
327 ' ', plr, NULL, vph, arg);
328 break;
329
330 case 'p':
331 ptr = va_arg(ap, void *);
332
333 if (ptr) {
334 len = local_itoa(num, (unsigned long int)ptr,
335 16, false);
336 err |= write_padded(num, len, pad,
337 plr ? ' ' : pch, plr,
338 prfx_hex, vph, arg);
339 }
340 else {
341 err |= write_padded(str_nil,
342 sizeof(str_nil) - 1,
343 pad, ' ', plr, NULL,
344 vph, arg);
345 }
346 break;
347
348 case 'r':
349 pl = va_arg(ap, const struct pl *);
350
351 err |= write_padded(pl ? pl->p : NULL,
352 (pl && pl->p) ? pl->l : 0,
353 pad, ' ', plr, NULL, vph, arg);
354 break;
355
356 case 's':
357 str = va_arg(ap, const char *);
358 err |= write_padded(str, str_len(str), pad,
359 ' ', plr, NULL, vph, arg);
360 break;
361
362 case 'X':
363 uc = true;
364 /*@fallthrough@*/
365 case 'x':
366 base = 16;
367 /*@fallthrough@*/
368 case 'u':
369 switch (lenmod) {
370
371 case LENMOD_SIZE:
372 n = va_arg(ap, size_t);
373 break;
374
375 default:
376 case LENMOD_LONG_LONG:
377 n = va_arg(ap, unsigned long long);
378 break;
379
380 case LENMOD_LONG:
381 n = va_arg(ap, unsigned long);
382 break;
383
384 case LENMOD_NONE:
385 n = va_arg(ap, unsigned);
386 break;
387 }
388
389 len = local_itoa(num, n, base, uc);
390
391 err |= write_padded(num, len, pad,
392 plr ? ' ' : pch, plr, NULL,
393 vph, arg);
394 break;
395
396 case 'v':
397 str = va_arg(ap, char *);
398 apl = va_arg(ap, va_list *);
399
400 if (!str || !apl)
401 break;
402
403 err |= re_vhprintf(str, *apl, vph, arg);
404 break;
405
406 case 'W':
407 uc = true;
408 /*@fallthrough@*/
409 case 'w':
410 bptr = va_arg(ap, uint8_t *);
411 len = va_arg(ap, size_t);
412
413 len = bptr ? len : 0;
414 pch = plr ? ' ' : pch;
415
416 while (!plr && pad-- > (len * 2))
417 err |= vph(&pch, 1, arg);
418
419 for (i=0; i<len; i++) {
420 const uint8_t v = *bptr++;
421 uint32_t l = local_itoa(num, v, 16, uc);
422 err |= write_padded(num, l, 2, '0',
423 false, NULL, vph, arg);
424 }
425
426 while (plr && pad-- > (len * 2))
427 err |= vph(&pch, 1, arg);
428
429 break;
430
431 case 'z':
432 lenmod = LENMOD_SIZE;
433 fm = true;
434 break;
435
436 case 'j':
437 sa = va_arg(ap, struct sa *);
438 if (!sa)
439 break;
440 if (sa_ntop(sa, addr, sizeof(addr))) {
441 err |= write_padded("?", 1, pad, ' ',
442 plr, NULL, vph, arg);
443 break;
444 }
445 err |= write_padded(addr, strlen(addr), pad, ' ',
446 plr, NULL, vph, arg);
447 break;
448
449
450 case 'J':
451 sa = va_arg(ap, struct sa *);
452 if (!sa)
453 break;
454 if (sa_ntop(sa, addr, sizeof(addr))) {
455 err |= write_padded("?", 1, pad, ' ',
456 plr, NULL, vph, arg);
457 break;
458 }
459
460#ifdef HAVE_INET6
461 if (AF_INET6 == sa_af(sa)) {
462 ch = '[';
463 err |= vph(&ch, 1, arg);
464 }
465#endif
466 err |= write_padded(addr, strlen(addr), pad, ' ',
467 plr, NULL, vph, arg);
468#ifdef HAVE_INET6
469 if (AF_INET6 == sa_af(sa)) {
470 ch = ']';
471 err |= vph(&ch, 1, arg);
472 }
473#endif
474
475 ch = ':';
476 err |= vph(&ch, 1, arg);
477 len = local_itoa(num, sa_port(sa), 10, false);
478 err |= write_padded(num, len, pad,
479 plr ? ' ' : pch, plr, NULL,
480 vph, arg);
481
482 break;
483
484 default:
485 if (('0' <= *p) && (*p <= '9')) {
486 if (!pad && ('0' == *p)) {
487 pch = '0';
488 }
489 else {
490 pad *= 10;
491 pad += *p - '0';
492 }
493 fm = true;
494 break;
495 }
496
497 ch = '?';
498
499 err |= vph(&ch, 1, arg);
500 break;
501 }
502
503 if (!fm)
504 p0 = p + 1;
505 }
506
507 if (!fm && p > p0)
508 err |= vph(p0, p - p0, arg);
509
510 return err;
511}
512
513
514static int print_handler(const char *p, size_t size, void *arg)
515{
516 struct pl *pl = arg;
517
518 if (size > pl->l)
519 return ENOMEM;
520
521 memcpy((void *)pl->p, p, size);
522
523 pl_advance(pl, size);
524
525 return 0;
526}
527
528
529struct dyn_print {
530 char *str;
531 char *p;
532 size_t l;
533 size_t size;
534};
535
536
537static int print_handler_dyn(const char *p, size_t size, void *arg)
538{
539 struct dyn_print *dp = arg;
540
541 if (size > dp->l - 1) {
542 const size_t new_size = MAX(dp->size + size, dp->size * 2);
543 char *str = mem_realloc(dp->str, new_size);
544 if (!str)
545 return ENOMEM;
546
547 dp->str = str;
548 dp->l += new_size - dp->size;
549 dp->p = dp->str + new_size - dp->l;
550 dp->size = new_size;
551 }
552
553 memcpy(dp->p, p, size);
554
555 dp->p += size;
556 dp->l -= size;
557
558 return 0;
559}
560
561
562struct strm_print {
563 FILE *f;
564 size_t n;
565};
566
567static int print_handler_stream(const char *p, size_t size, void *arg)
568{
569 struct strm_print *sp = arg;
570
571 if (1 != fwrite(p, size, 1, sp->f))
572 return ENOMEM;
573
574 sp->n += size;
575
576 return 0;
577}
578
579
580/**
581 * Print a formatted string to a file stream, using va_list
582 *
583 * @param stream File stream for the output
584 * @param fmt Formatted string
585 * @param ap Variable-arguments list
586 *
587 * @return The number of characters printed, or -1 if error
588 */
589int re_vfprintf(FILE *stream, const char *fmt, va_list ap)
590{
591 struct strm_print sp;
592
593 if (!stream)
594 return -1;
595
596 sp.f = stream;
597 sp.n = 0;
598
599 if (0 != re_vhprintf(fmt, ap, print_handler_stream, &sp))
600 return -1;
601
602 return (int)sp.n;
603}
604
605
606/**
607 * Print a formatted string to stdout, using va_list
608 *
609 * @param fmt Formatted string
610 * @param ap Variable-arguments list
611 *
612 * @return The number of characters printed, or -1 if error
613 */
614int re_vprintf(const char *fmt, va_list ap)
615{
616 return re_vfprintf(stdout, fmt, ap);
617}
618
619
620/**
621 * Print a formatted string to a buffer, using va_list
622 *
623 * @param str Buffer for output string
624 * @param size Size of buffer
625 * @param fmt Formatted string
626 * @param ap Variable-arguments list
627 *
628 * @return The number of characters printed, or -1 if error
629 */
630int re_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
631{
632 struct pl pl;
633 int err;
634
635 if (!str || !size)
636 return -1;
637
638 pl.p = str;
639 pl.l = size - 1;
640
641 err = re_vhprintf(fmt, ap, print_handler, &pl);
642
643 str[size - pl.l - 1] = '\0';
644
645 return err ? -1 : (int)(size - pl.l - 1);
646}
647
648
649/**
650 * Print a formatted string to a dynamically allocated buffer, using va_list
651 *
652 * @param strp Pointer for output string
653 * @param fmt Formatted string
654 * @param ap Variable-arguments list
655 *
656 * @return 0 if success, otherwise errorcode
657 */
658int re_vsdprintf(char **strp, const char *fmt, va_list ap)
659{
660 struct dyn_print dp;
661 int err;
662
663 if (!strp)
664 return EINVAL;
665
666 dp.size = 16;
667 dp.str = mem_alloc(dp.size, NULL);
668 if (!dp.str)
669 return ENOMEM;
670
671 dp.p = dp.str;
672 dp.l = dp.size;
673
674 err = re_vhprintf(fmt, ap, print_handler_dyn, &dp);
675 if (err)
676 goto out;
677
678 *dp.p = '\0';
679
680 out:
681 if (err)
682 mem_deref(dp.str);
683 else
684 *strp = dp.str;
685
686 return err;
687}
688
689
690/**
691 * Print a formatted string
692 *
693 * @param pf Print backend
694 * @param fmt Formatted string
695 *
696 * @return 0 if success, otherwise errorcode
697 */
698int re_hprintf(struct re_printf *pf, const char *fmt, ...)
699{
700 va_list ap;
701 int err;
702
703 if (!pf)
704 return EINVAL;
705
706 va_start(ap, fmt);
707 err = re_vhprintf(fmt, ap, pf->vph, pf->arg);
708 va_end(ap);
709
710 return err;
711}
712
713
714/**
715 * Print a formatted string to a file stream
716 *
717 * @param stream File stream for output
718 * @param fmt Formatted string
719 *
720 * @return The number of characters printed, or -1 if error
721 */
722int re_fprintf(FILE *stream, const char *fmt, ...)
723{
724 va_list ap;
725 int n;
726
727 va_start(ap, fmt);
728 n = re_vfprintf(stream, fmt, ap);
729 va_end(ap);
730
731 return n;
732}
733
734
735/**
736 * Print a formatted string to stdout
737 *
738 * @param fmt Formatted string
739 *
740 * @return The number of characters printed, or -1 if error
741 */
742int re_printf(const char *fmt, ...)
743{
744 va_list ap;
745 int n;
746
747 va_start(ap, fmt);
748 n = re_vprintf(fmt, ap);
749 va_end(ap);
750
751 return n;
752}
753
754
755/**
756 * Print a formatted string to a buffer
757 *
758 * @param str Buffer for output string
759 * @param size Size of buffer
760 * @param fmt Formatted string
761 *
762 * @return The number of characters printed, or -1 if error
763 */
764int re_snprintf(char *str, size_t size, const char *fmt, ...)
765{
766 va_list ap;
767 int n;
768
769 va_start(ap, fmt);
770 n = re_vsnprintf(str, size, fmt, ap);
771 va_end(ap);
772
773 return n;
774}
775
776
777/**
778 * Print a formatted string to a buffer
779 *
780 * @param strp Buffer pointer for output string
781 * @param fmt Formatted string
782 *
783 * @return 0 if success, otherwise errorcode
784 */
785int re_sdprintf(char **strp, const char *fmt, ...)
786{
787 va_list ap;
788 int err;
789
790 va_start(ap, fmt);
791 err = re_vsdprintf(strp, fmt, ap);
792 va_end(ap);
793
794 return err;
795}