#include "config.h"
-#include <assert.h>
-#include <ctype.h>
-#include <math.h>
#include <stdarg.h>
+#include <stddef.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_FLOAT_H
-# include <float.h>
-#endif
-
-#include "darray.h"
#include "dstr.h"
-
-/*----- Tunable constants -------------------------------------------------*/
-
-/*
- * For each format specifier, at least @PUTFSTEP@ bytes are ensured before
- * writing the formatted result.
- */
-
-#define PUTFSTEP 64 /* Buffer size for @putf@ */
-
-/*----- Preliminary definitions -------------------------------------------*/
-
-#define OUTPUT_FMTTYPES(_) \
- _(i, unsigned int) \
- _(li, unsigned long) \
- _(s, char *) \
- _(p, void *) \
- _(f, double) \
- _(Lf, long double)
-
-#define PERCENT_N_FMTTYPES(_) \
- _(hn, short *) \
- _(n, int *) \
- _(ln, long *)
-
-#define FMTTYPES(_) \
- OUTPUT_FMTTYPES(_) \
- PERCENT_N_FMTTYPES(_)
-
-enum {
- fmt_unset = 0,
-#define CODE(code, ty) fmt_##code,
- FMTTYPES(CODE)
-#undef CODE
- fmt__limit
-};
-
-typedef struct {
- int fmt;
- union {
-#define MEMB(code, ty) ty code;
- FMTTYPES(MEMB)
-#undef MEMB
- } u;
-} fmtarg;
-
-DA_DECL(fmtarg_v, fmtarg);
-
-enum {
- len_std = 0,
- len_h,
- len_l,
- len_ll,
- len_L
-};
-
-#define f_len 0x000fu
-#define f_wd 0x0010u
-#define f_wdarg 0x0020u
-#define f_prec 0x0040u
-#define f_precarg 0x0080u
-#define f_plus 0x0100u
-#define f_minus 0x0200u
-#define f_sharp 0x0400u
-#define f_zero 0x0800u
-#define f_posarg 0x1000u
-
-typedef struct {
- const char *p;
- size_t n;
- unsigned f;
- int fmt, ch;
- int wd, prec;
- int arg;
-} fmtspec;
-
-DA_DECL(fmtspec_v, fmtspec);
+#include "gprintf.h"
/*----- Main code ---------------------------------------------------------*/
* supplied functions with @printf@-style interfaces.
*/
-static void set_arg(fmtarg_v *av, size_t i, int fmt)
-{
- size_t j, n;
+static int putch(void *out, int ch)
+ { dstr *d = out; DPUTC(d, ch); return (0); }
+static int putm(void *out, const char *p, size_t sz)
+ { dstr *d = out; DPUTM(d, p, sz); return (0); }
- n = DA_LEN(av);
- if (i >= n) {
- DA_ENSURE(av, i + 1 - n);
- for (j = n; j <= i; j++) DA(av)[j].fmt = fmt_unset;
- DA_UNSAFE_EXTEND(av, i + 1 - n);
- }
-
- if (DA(av)[i].fmt == fmt_unset) DA(av)[i].fmt = fmt;
- else assert(DA(av)[i].fmt == fmt);
-}
-
-int dstr_vputf(dstr *d, const char *p, va_list *ap)
+static int nputf(void *out, size_t maxsz, const char *p, ...)
{
- size_t n = d->len;
- size_t sz, mx;
- dstr dd = DSTR_INIT;
- fmtspec_v sv = DA_INIT;
- fmtarg_v av = DA_INIT;
- fmtarg *fa, *fal;
- fmtspec *fs, *fsl;
- unsigned f;
- int i, anext;
- int wd, prec;
-
- /* --- Initial pass through the input, parsing format specifiers --- *
- *
- * We essentially compile the format string into a vector of @fmtspec@
- * objects, each of which represnts a chunk of literal text followed by a
- * (possibly imaginary, in the case of the final one) formatting directive.
- * Output then simply consists of interpreting these specifiers in order.
- */
-
- anext = 0;
-
- while (*p) {
- f = 0;
- DA_ENSURE(&sv, 1);
- fs = &DA(&sv)[DA_LEN(&sv)];
- DA_UNSAFE_EXTEND(&sv, 1);
-
- /* --- Find the end of this literal portion --- */
-
- fs->p = p;
- while (*p && *p != '%') p++;
- fs->n = p - fs->p;
-
- /* --- Some simple cases --- *
- *
- * We might have reached the end of the string, or maybe a `%%' escape.
- */
-
- if (!*p) { fs->fmt = fmt_unset; fs->ch = 0; break; }
- p++;
- if (*p == '%') { fs->fmt = fmt_unset; fs->ch = '%'; p++; continue; }
-
- /* --- Pick up initial flags --- */
-
- flags:
- for (;;) {
- switch (*p) {
- case '+': f |= f_plus; break;
- case '-': f |= f_minus; break;
- case '#': f |= f_sharp; break;
- case '0': f |= f_zero; break;
- default: goto done_flags;
- }
- p++;
- }
-
- /* --- Pick up the field width --- */
-
- done_flags:
- i = 0;
- while (isdigit((unsigned char)*p)) i = 10*i + *p++ - '0';
-
- /* --- Snag: this might have been an argument position indicator --- */
-
- if (i && *p == '$' && (!f || f == f_zero)) {
- f |= f_posarg;
- fs->arg = i - 1;
- p++;
- goto flags;
- }
-
- /* --- Set the field width --- *
- *
- * If @i@ is nonzero here then we have a numeric field width. Otherwise
- * it might be `*', maybe with an explicit argument number.
- */
-
- if (i) {
- f |= f_wd;
- fs->wd = i;
- } else if (*p == '*') {
- p++;
- if (!isdigit((unsigned char)*p))
- i = anext++;
- else {
- i = *p++ - '0';
- while (isdigit((unsigned char)*p)) i = 10*i + *p++ - '0';
- assert(*p == '$'); p++;
- assert(i > 0); i--;
- }
- f |= f_wd | f_wdarg;
- set_arg(&av, i, fmt_i); fs->wd = i;
- }
-
- /* --- Maybe we have a precision spec --- */
-
- if (*p == '.') {
- p++;
- f |= f_prec;
- if (isdigit((unsigned char)*p)) {
- i = *p++ - '0';
- while (isdigit((unsigned char)*p)) i = 10*i + *p++ - '0';
- fs->prec = i;
- } else if (*p != '*')
- fs->prec = 0;
- else {
- p++;
- if (!isdigit((unsigned char)*p))
- i = anext++;
- else {
- i = *p++ - '0';
- while (isdigit((unsigned char)*p)) i = 10*i + *p++ - '0';
- assert(*p == '$'); p++;
- assert(i > 0); i--;
- }
- f |= f_precarg;
- set_arg(&av, i, fmt_i); fs->prec = i;
- }
- }
-
- /* --- Maybe some length flags --- */
-
- switch (*p) {
- case 'h': f |= len_h; p++; break;
- case 'l': f |= len_l; p++; break;
- case 'L': f |= len_L; p++; break;
- }
-
- /* --- The flags are now ready --- */
-
- fs->f = f;
-
- /* --- At the end, an actual directive --- */
-
- fs->ch = *p;
- switch (*p++) {
- case '%':
- fs->fmt = fmt_unset;
- break;
- case 'd': case 'i': case 'x': case 'X': case 'o': case 'u':
- switch (f & f_len) {
- case len_l: fs->fmt = fmt_li; break;
- default: fs->fmt = fmt_i;
- }
- break;
- case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
- fs->fmt = (f & f_len) == len_L ? fmt_Lf : fmt_f;
- break;
- case 'c':
- fs->fmt = fmt_i;
- break;
- case 's':
- fs->fmt = fmt_s;
- break;
- case 'p':
- fs->fmt = fmt_p;
- break;
- case 'n':
- switch (f & f_len) {
- case len_h: fs->fmt = fmt_hn; break;
- case len_l: fs->fmt = fmt_ln; break;
- default: fs->fmt = fmt_n;
- }
- break;
- default:
- fprintf(stderr,
- "FATAL dstr_vputf: unknown format specifier `%c'\n", p[-1]);
- abort();
- }
-
- /* --- Finally sort out the argument --- *
- *
- * If we don't have explicit argument positions then this comes after the
- * width and precision; and we don't know the type code until we've
- * parsed the specifier, so this seems the right place to handle it.
- */
-
- if (!(f & f_posarg)) fs->arg = anext++;
- set_arg(&av, fs->arg, fs->fmt);
- }
-
- /* --- Quick pass over the argument vector to collect the arguments --- */
-
- for (fa = DA(&av), fal = fa + DA_LEN(&av); fa < fal; fa++) {
- switch (fa->fmt) {
-#define CASE(code, ty) case fmt_##code: fa->u.code = va_arg(*ap, ty); break;
- FMTTYPES(CASE)
-#undef CASE
- default: abort();
- }
- }
-
- /* --- Final pass through the format string to produce output --- */
-
- fa = DA(&av);
- for (fs = DA(&sv), fsl = fs + DA_LEN(&sv); fs < fsl; fs++) {
- f = fs->f;
-
- /* --- Output the literal portion --- */
-
- if (fs->n) DPUTM(d, fs->p, fs->n);
-
- /* --- And now the variable portion --- */
-
- if (fs->fmt == fmt_unset) {
- switch (fs->ch) {
- case 0: break;
- case '%': DPUTC(d, '%'); break;
- default: abort();
- }
- continue;
- }
-
- DRESET(&dd);
- DPUTC(&dd, '%');
-
- /* --- Resolve the width and precision --- */
-
- if (!(f & f_wd))
- wd = 0;
- else {
- wd = (fs->f & f_wdarg) ? *(int *)&fa[fs->wd].u.i : fs->wd;
- if (wd < 0) { wd = -wd; f |= f_minus; }
- }
-
- if (!(f & f_prec))
- prec = 0;
- else {
- prec = (fs->f & f_precarg) ? *(int *)&fa[fs->prec].u.i : fs->prec;
- if (prec < 0) { prec = 0; f &= ~f_prec; }
- }
-
- /* --- Write out the flags, width and precision --- */
-
- if (f & f_plus) DPUTC(&dd, '+');
- if (f & f_minus) DPUTC(&dd, '-');
- if (f & f_sharp) DPUTC(&dd, '#');
- if (f & f_zero) DPUTC(&dd, '0');
-
- if (f & f_wd) {
- DENSURE(&dd, PUTFSTEP);
- dd.len += sprintf(dd.buf + dd.len, "%d", wd);
- }
-
- if (f & f_prec) {
- DENSURE(&dd, PUTFSTEP + 1);
- dd.len += sprintf(dd.buf + dd.len, ".%d", prec);
- }
-
- /* --- Write out the length gadget --- */
-
- switch (f & f_len) {
- case len_h: DPUTC(&dd, 'h'); break;
- case len_l: DPUTC(&dd, 'l'); break;
- case len_L: DPUTC(&dd, 'L'); break;
- case len_std: break;
- default: abort();
- }
-
- /* --- And finally the actually important bit --- */
-
- DPUTC(&dd, fs->ch);
- DPUTZ(&dd);
-
- /* --- Make sure we have enough space for the output --- */
-
- sz = PUTFSTEP;
- if (sz < wd) sz = wd;
- if (sz < prec + 16) sz = prec + 16;
- switch (fs->ch) {
- case 'a': case 'A':
- case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
-#ifdef HAVE_FLOAT_H
- if (fs->ch == 'f') {
- mx = ((fs->f & f_len) == len_L ?
- LDBL_MAX_10_EXP : DBL_MAX_10_EXP) + 16;
- if (sz < mx) sz = mx;
- }
- break;
-#else
- DPUTS(d, "<no float support>");
- continue;
-#endif
- case 's':
- if (!(f & f_prec)) {
- n = strlen(fa[fs->arg].u.s);
- if (sz < n) sz = n;
- }
- break;
- case 'n':
- switch (fs->fmt) {
-#define CASE(code, ty) \
- case fmt_##code: *fa[fs->arg].u.code = d->len - n; break;
- PERCENT_N_FMTTYPES(CASE)
-#undef CASE
- default: abort();
- }
- continue;
- }
-
- /* --- Finally do the output stage --- */
+ dstr *d = out;
+ va_list ap;
+ int n;
- DENSURE(d, sz + 1);
- switch (fs->fmt) {
+ va_start(ap, p);
+ DENSURE(d, maxsz + 1);
#ifdef HAVE_SNPRINTF
-# define CASE(code, ty) case fmt_##code: \
- i = snprintf(d->buf + d->len, sz + 1, dd.buf, fa[fs->arg].u.code); \
- break;
+ n = vsnprintf(d->buf + d->len, maxsz + 1, p, ap);
#else
-# define CASE(code, ty) case fmt_##code: \
- i = sprintf(d->buf + d->len, dd.buf, fa[fs->arg].u.code); \
- break;
+ n = vsprintf(d->buf + d->len, p, ap);
#endif
- OUTPUT_FMTTYPES(CASE)
-#undef CASE
- default: abort();
- }
- assert(0 <= i && i <= sz); d->len += i;
- }
+ assert(0 <= n && n <= maxsz);
+ va_end(ap); d->len += n; return (n);
+}
- /* --- We're done --- */
+const struct gprintf_ops dstr_printops =
+ { putch, putm, nputf };
- DPUTZ(d);
- DDESTROY(&dd);
- return (d->len - n);
-}
+int dstr_vputf(dstr *d, const char *p, va_list *ap)
+ { int n = vgprintf(&dstr_printops, d, p, ap); DPUTZ(d); return (n); }
/* --- @dstr_putf@ --- *
*