Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/fmt/print.c b/src/fmt/print.c
new file mode 100644
index 0000000..85e9ebf
--- /dev/null
+++ b/src/fmt/print.c
@@ -0,0 +1,790 @@
+/**
+ * @file fmt/print.c Formatted printing
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_sa.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <math.h>
+#ifdef _MSC_VER
+#include <float.h>
+#ifndef isinf
+#define isinf(d) (!_finite(d))
+#endif
+#ifndef isnan
+#define isnan(d) _isnan(d)
+#endif
+#endif
+#ifdef SOLARIS
+#include <ieeefp.h>
+#undef isinf
+#define isinf(a) (fpclass((a)) == FP_NINF || fpclass((a)) == FP_PINF)
+#undef isnan
+#define isnan(a) isnand((a))
+#endif
+
+
+enum length_modifier {
+ LENMOD_NONE = 0,
+ LENMOD_LONG = 1,
+ LENMOD_LONG_LONG = 2,
+ LENMOD_SIZE = 42,
+};
+
+enum {
+ DEC_SIZE = 42,
+ NUM_SIZE = 64
+};
+
+static const char prfx_neg[] = "-";
+static const char prfx_hex[] = "0x";
+static const char str_nil[] = "(nil)";
+
+
+static int write_padded(const char *p, size_t sz, size_t pad, char pch,
+ bool plr, const char *prfx, re_vprintf_h *vph,
+ void *arg)
+{
+ const size_t prfx_len = str_len(prfx);
+ int err = 0;
+
+ pad -= MIN(pad, prfx_len);
+
+ if (prfx && pch == '0')
+ err |= vph(prfx, prfx_len, arg);
+
+ while (!plr && (pad-- > sz))
+ err |= vph(&pch, 1, arg);
+
+ if (prfx && pch != '0')
+ err |= vph(prfx, prfx_len, arg);
+
+ if (p && sz)
+ err |= vph(p, sz, arg);
+
+ while (plr && pad-- > sz)
+ err |= vph(&pch, 1, arg);
+
+ return err;
+}
+
+
+static uint32_t local_itoa(char *buf, uint64_t n, uint8_t base, bool uc)
+{
+ char c, *p = buf + NUM_SIZE;
+ uint32_t len = 1;
+ const char a = uc ? 'A' : 'a';
+
+ *--p = '\0';
+ do {
+ const uint64_t dv = n / base;
+ const uint64_t mul = dv * base;
+
+ c = (char)(n - mul);
+
+ if (c < 10)
+ *--p = '0' + c;
+ else
+ *--p = a + (c - 10);
+
+ n = dv;
+ ++len;
+
+ } while (n != 0);
+
+ memmove(buf, p, len);
+
+ return len - 1;
+}
+
+
+static size_t local_ftoa(char *buf, double n, size_t dp)
+{
+ char *p = buf;
+ long long a = (long long)n;
+ double b = n - (double)a;
+
+ b = (b < 0) ? -b : b;
+
+ /* integral part */
+ p += local_itoa(p, (a < 0) ? -a : a, 10, false);
+
+ *p++ = '.';
+
+ /* decimal digits */
+ while (dp--) {
+ char v;
+
+ b *= 10;
+ v = (char)b;
+ b -= v;
+
+ *p++ = '0' + (char)v;
+ }
+
+ *p = '\0';
+
+ return p - buf;
+}
+
+
+/**
+ * Print a formatted string
+ *
+ * @param fmt Formatted string
+ * @param ap Variable argument
+ * @param vph Print handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ *
+ * Extensions:
+ *
+ * <pre>
+ * %b (char *, size_t) Buffer string with pointer and length
+ * %r (struct pl) Pointer-length object
+ * %w (uint8_t *, size_t) Binary buffer to hexadecimal format
+ * %j (struct sa *) Socket address - address part only
+ * %J (struct sa *) Socket address and port - like 1.2.3.4:1234
+ * %H (re_printf_h *, void *) Print handler with argument
+ * %v (char *fmt, va_list *) Variable argument list
+ * %m (int) Describe an error code
+ * </pre>
+ *
+ * Reserved for the future:
+ *
+ * %k
+ * %y
+ *
+ */
+int re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg)
+{
+ uint8_t base, *bptr;
+ char pch, ch, num[NUM_SIZE], addr[64], msg[256];
+ enum length_modifier lenmod = LENMOD_NONE;
+ struct re_printf pf;
+ bool fm = false, plr = false;
+ const struct pl *pl;
+ size_t pad = 0, fpad = -1, len, i;
+ const char *str, *p = fmt, *p0 = fmt;
+ const struct sa *sa;
+ re_printf_h *ph;
+ void *ph_arg;
+ va_list *apl;
+ int err = 0;
+ void *ptr;
+ uint64_t n;
+ int64_t sn;
+ bool uc = false;
+ double dbl;
+
+ if (!fmt || !vph)
+ return EINVAL;
+
+ pf.vph = vph;
+ pf.arg = arg;
+
+ for (;*p && !err; p++) {
+
+ if (!fm) {
+ if (*p != '%')
+ continue;
+
+ pch = ' ';
+ plr = false;
+ pad = 0;
+ fpad = -1;
+ lenmod = LENMOD_NONE;
+ uc = false;
+
+ if (p > p0)
+ err |= vph(p0, p - p0, arg);
+
+ fm = true;
+ continue;
+ }
+
+ fm = false;
+ base = 10;
+
+ switch (*p) {
+
+ case '-':
+ plr = true;
+ fm = true;
+ break;
+
+ case '.':
+ fpad = pad;
+ pad = 0;
+ fm = true;
+ break;
+
+ case '%':
+ ch = '%';
+
+ err |= vph(&ch, 1, arg);
+ break;
+
+ case 'b':
+ str = va_arg(ap, const char *);
+ len = va_arg(ap, size_t);
+
+ err |= write_padded(str, str ? len : 0, pad, ' ',
+ plr, NULL, vph, arg);
+ break;
+
+ case 'c':
+ ch = va_arg(ap, int);
+
+ err |= write_padded(&ch, 1, pad, ' ', plr, NULL,
+ vph, arg);
+ break;
+
+ case 'd':
+ case 'i':
+ switch (lenmod) {
+
+ case LENMOD_SIZE:
+ sn = va_arg(ap, ssize_t);
+ break;
+
+ default:
+ case LENMOD_LONG_LONG:
+ sn = va_arg(ap, signed long long);
+ break;
+
+ case LENMOD_LONG:
+ sn = va_arg(ap, signed long);
+ break;
+
+ case LENMOD_NONE:
+ sn = va_arg(ap, signed);
+ break;
+ }
+
+ len = local_itoa(num, (sn < 0) ? -sn : sn, base,
+ false);
+
+ err |= write_padded(num, len, pad,
+ plr ? ' ' : pch, plr,
+ (sn < 0) ? prfx_neg : NULL,
+ vph, arg);
+ break;
+
+ case 'f':
+ case 'F':
+ dbl = va_arg(ap, double);
+
+ if (fpad == (size_t)-1) {
+ fpad = pad;
+ pad = 0;
+ }
+
+ if (isinf(dbl)) {
+ err |= write_padded("inf", 3, fpad,
+ ' ', plr, NULL, vph, arg);
+ }
+ else if (isnan(dbl)) {
+ err |= write_padded("nan", 3, fpad,
+ ' ', plr, NULL, vph, arg);
+ }
+ else {
+ len = local_ftoa(num, dbl,
+ pad ? min(pad, DEC_SIZE) : 6);
+
+ err |= write_padded(num, len, fpad,
+ plr ? ' ' : pch, plr,
+ (dbl<0) ? prfx_neg : NULL,
+ vph, arg);
+ }
+ break;
+
+ case 'H':
+ ph = va_arg(ap, re_printf_h *);
+ ph_arg = va_arg(ap, void *);
+
+ if (ph)
+ err |= ph(&pf, ph_arg);
+ break;
+
+ case 'l':
+ ++lenmod;
+ fm = true;
+ break;
+
+ case 'm':
+ str = str_error(va_arg(ap, int), msg, sizeof(msg));
+ err |= write_padded(str, str_len(str), pad,
+ ' ', plr, NULL, vph, arg);
+ break;
+
+ case 'p':
+ ptr = va_arg(ap, void *);
+
+ if (ptr) {
+ len = local_itoa(num, (unsigned long int)ptr,
+ 16, false);
+ err |= write_padded(num, len, pad,
+ plr ? ' ' : pch, plr,
+ prfx_hex, vph, arg);
+ }
+ else {
+ err |= write_padded(str_nil,
+ sizeof(str_nil) - 1,
+ pad, ' ', plr, NULL,
+ vph, arg);
+ }
+ break;
+
+ case 'r':
+ pl = va_arg(ap, const struct pl *);
+
+ err |= write_padded(pl ? pl->p : NULL,
+ (pl && pl->p) ? pl->l : 0,
+ pad, ' ', plr, NULL, vph, arg);
+ break;
+
+ case 's':
+ str = va_arg(ap, const char *);
+ err |= write_padded(str, str_len(str), pad,
+ ' ', plr, NULL, vph, arg);
+ break;
+
+ case 'X':
+ uc = true;
+ /*@fallthrough@*/
+ case 'x':
+ base = 16;
+ /*@fallthrough@*/
+ case 'u':
+ switch (lenmod) {
+
+ case LENMOD_SIZE:
+ n = va_arg(ap, size_t);
+ break;
+
+ default:
+ case LENMOD_LONG_LONG:
+ n = va_arg(ap, unsigned long long);
+ break;
+
+ case LENMOD_LONG:
+ n = va_arg(ap, unsigned long);
+ break;
+
+ case LENMOD_NONE:
+ n = va_arg(ap, unsigned);
+ break;
+ }
+
+ len = local_itoa(num, n, base, uc);
+
+ err |= write_padded(num, len, pad,
+ plr ? ' ' : pch, plr, NULL,
+ vph, arg);
+ break;
+
+ case 'v':
+ str = va_arg(ap, char *);
+ apl = va_arg(ap, va_list *);
+
+ if (!str || !apl)
+ break;
+
+ err |= re_vhprintf(str, *apl, vph, arg);
+ break;
+
+ case 'W':
+ uc = true;
+ /*@fallthrough@*/
+ case 'w':
+ bptr = va_arg(ap, uint8_t *);
+ len = va_arg(ap, size_t);
+
+ len = bptr ? len : 0;
+ pch = plr ? ' ' : pch;
+
+ while (!plr && pad-- > (len * 2))
+ err |= vph(&pch, 1, arg);
+
+ for (i=0; i<len; i++) {
+ const uint8_t v = *bptr++;
+ uint32_t l = local_itoa(num, v, 16, uc);
+ err |= write_padded(num, l, 2, '0',
+ false, NULL, vph, arg);
+ }
+
+ while (plr && pad-- > (len * 2))
+ err |= vph(&pch, 1, arg);
+
+ break;
+
+ case 'z':
+ lenmod = LENMOD_SIZE;
+ fm = true;
+ break;
+
+ case 'j':
+ sa = va_arg(ap, struct sa *);
+ if (!sa)
+ break;
+ if (sa_ntop(sa, addr, sizeof(addr))) {
+ err |= write_padded("?", 1, pad, ' ',
+ plr, NULL, vph, arg);
+ break;
+ }
+ err |= write_padded(addr, strlen(addr), pad, ' ',
+ plr, NULL, vph, arg);
+ break;
+
+
+ case 'J':
+ sa = va_arg(ap, struct sa *);
+ if (!sa)
+ break;
+ if (sa_ntop(sa, addr, sizeof(addr))) {
+ err |= write_padded("?", 1, pad, ' ',
+ plr, NULL, vph, arg);
+ break;
+ }
+
+#ifdef HAVE_INET6
+ if (AF_INET6 == sa_af(sa)) {
+ ch = '[';
+ err |= vph(&ch, 1, arg);
+ }
+#endif
+ err |= write_padded(addr, strlen(addr), pad, ' ',
+ plr, NULL, vph, arg);
+#ifdef HAVE_INET6
+ if (AF_INET6 == sa_af(sa)) {
+ ch = ']';
+ err |= vph(&ch, 1, arg);
+ }
+#endif
+
+ ch = ':';
+ err |= vph(&ch, 1, arg);
+ len = local_itoa(num, sa_port(sa), 10, false);
+ err |= write_padded(num, len, pad,
+ plr ? ' ' : pch, plr, NULL,
+ vph, arg);
+
+ break;
+
+ default:
+ if (('0' <= *p) && (*p <= '9')) {
+ if (!pad && ('0' == *p)) {
+ pch = '0';
+ }
+ else {
+ pad *= 10;
+ pad += *p - '0';
+ }
+ fm = true;
+ break;
+ }
+
+ ch = '?';
+
+ err |= vph(&ch, 1, arg);
+ break;
+ }
+
+ if (!fm)
+ p0 = p + 1;
+ }
+
+ if (!fm && p > p0)
+ err |= vph(p0, p - p0, arg);
+
+ return err;
+}
+
+
+static int print_handler(const char *p, size_t size, void *arg)
+{
+ struct pl *pl = arg;
+
+ if (size > pl->l)
+ return ENOMEM;
+
+ memcpy((void *)pl->p, p, size);
+
+ pl_advance(pl, size);
+
+ return 0;
+}
+
+
+struct dyn_print {
+ char *str;
+ char *p;
+ size_t l;
+ size_t size;
+};
+
+
+static int print_handler_dyn(const char *p, size_t size, void *arg)
+{
+ struct dyn_print *dp = arg;
+
+ if (size > dp->l - 1) {
+ const size_t new_size = MAX(dp->size + size, dp->size * 2);
+ char *str = mem_realloc(dp->str, new_size);
+ if (!str)
+ return ENOMEM;
+
+ dp->str = str;
+ dp->l += new_size - dp->size;
+ dp->p = dp->str + new_size - dp->l;
+ dp->size = new_size;
+ }
+
+ memcpy(dp->p, p, size);
+
+ dp->p += size;
+ dp->l -= size;
+
+ return 0;
+}
+
+
+struct strm_print {
+ FILE *f;
+ size_t n;
+};
+
+static int print_handler_stream(const char *p, size_t size, void *arg)
+{
+ struct strm_print *sp = arg;
+
+ if (1 != fwrite(p, size, 1, sp->f))
+ return ENOMEM;
+
+ sp->n += size;
+
+ return 0;
+}
+
+
+/**
+ * Print a formatted string to a file stream, using va_list
+ *
+ * @param stream File stream for the output
+ * @param fmt Formatted string
+ * @param ap Variable-arguments list
+ *
+ * @return The number of characters printed, or -1 if error
+ */
+int re_vfprintf(FILE *stream, const char *fmt, va_list ap)
+{
+ struct strm_print sp;
+
+ if (!stream)
+ return -1;
+
+ sp.f = stream;
+ sp.n = 0;
+
+ if (0 != re_vhprintf(fmt, ap, print_handler_stream, &sp))
+ return -1;
+
+ return (int)sp.n;
+}
+
+
+/**
+ * Print a formatted string to stdout, using va_list
+ *
+ * @param fmt Formatted string
+ * @param ap Variable-arguments list
+ *
+ * @return The number of characters printed, or -1 if error
+ */
+int re_vprintf(const char *fmt, va_list ap)
+{
+ return re_vfprintf(stdout, fmt, ap);
+}
+
+
+/**
+ * Print a formatted string to a buffer, using va_list
+ *
+ * @param str Buffer for output string
+ * @param size Size of buffer
+ * @param fmt Formatted string
+ * @param ap Variable-arguments list
+ *
+ * @return The number of characters printed, or -1 if error
+ */
+int re_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
+{
+ struct pl pl;
+ int err;
+
+ if (!str || !size)
+ return -1;
+
+ pl.p = str;
+ pl.l = size - 1;
+
+ err = re_vhprintf(fmt, ap, print_handler, &pl);
+
+ str[size - pl.l - 1] = '\0';
+
+ return err ? -1 : (int)(size - pl.l - 1);
+}
+
+
+/**
+ * Print a formatted string to a dynamically allocated buffer, using va_list
+ *
+ * @param strp Pointer for output string
+ * @param fmt Formatted string
+ * @param ap Variable-arguments list
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int re_vsdprintf(char **strp, const char *fmt, va_list ap)
+{
+ struct dyn_print dp;
+ int err;
+
+ if (!strp)
+ return EINVAL;
+
+ dp.size = 16;
+ dp.str = mem_alloc(dp.size, NULL);
+ if (!dp.str)
+ return ENOMEM;
+
+ dp.p = dp.str;
+ dp.l = dp.size;
+
+ err = re_vhprintf(fmt, ap, print_handler_dyn, &dp);
+ if (err)
+ goto out;
+
+ *dp.p = '\0';
+
+ out:
+ if (err)
+ mem_deref(dp.str);
+ else
+ *strp = dp.str;
+
+ return err;
+}
+
+
+/**
+ * Print a formatted string
+ *
+ * @param pf Print backend
+ * @param fmt Formatted string
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int re_hprintf(struct re_printf *pf, const char *fmt, ...)
+{
+ va_list ap;
+ int err;
+
+ if (!pf)
+ return EINVAL;
+
+ va_start(ap, fmt);
+ err = re_vhprintf(fmt, ap, pf->vph, pf->arg);
+ va_end(ap);
+
+ return err;
+}
+
+
+/**
+ * Print a formatted string to a file stream
+ *
+ * @param stream File stream for output
+ * @param fmt Formatted string
+ *
+ * @return The number of characters printed, or -1 if error
+ */
+int re_fprintf(FILE *stream, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = re_vfprintf(stream, fmt, ap);
+ va_end(ap);
+
+ return n;
+}
+
+
+/**
+ * Print a formatted string to stdout
+ *
+ * @param fmt Formatted string
+ *
+ * @return The number of characters printed, or -1 if error
+ */
+int re_printf(const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = re_vprintf(fmt, ap);
+ va_end(ap);
+
+ return n;
+}
+
+
+/**
+ * Print a formatted string to a buffer
+ *
+ * @param str Buffer for output string
+ * @param size Size of buffer
+ * @param fmt Formatted string
+ *
+ * @return The number of characters printed, or -1 if error
+ */
+int re_snprintf(char *str, size_t size, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = re_vsnprintf(str, size, fmt, ap);
+ va_end(ap);
+
+ return n;
+}
+
+
+/**
+ * Print a formatted string to a buffer
+ *
+ * @param strp Buffer pointer for output string
+ * @param fmt Formatted string
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int re_sdprintf(char **strp, const char *fmt, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, fmt);
+ err = re_vsdprintf(strp, fmt, ap);
+ va_end(ap);
+
+ return err;
+}