/** * 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 to a memory buffer * * @param mb Memory buffer * @param fmt Formatted string * * @return 0 if success, otherwise errorcode */ int mbuf_printf(struct mbuf *mb, const char *fmt, ...) { int err = 0; va_list ap; va_start(ap, fmt); err = re_vhprintf(fmt, ap, vprintf_handler, mb); va_end(ap); 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 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 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) { char buf[4096]; /* TODO: avoid static, use print_handler_dyn ? */ struct pl pl; size_t n; if (!stream) return -1; pl.p = buf; pl.l = sizeof(buf); if (0 != re_vhprintf(fmt, ap, print_handler, &pl)) return -1; n = sizeof(buf) - pl.l; if (1 != fwrite(buf, n, 1, stream)) return -1; return (int)n; }
/** * Print a formatted variable argument list to a memory buffer * * @param mb Memory buffer * @param fmt Formatted string * @param ap Variable argument list * * @return 0 if success, otherwise errorcode */ int mbuf_vprintf(struct mbuf *mb, const char *fmt, va_list ap) { return re_vhprintf(fmt, ap, vprintf_handler, mb); }
/** * 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; }