/* xfprintf.c - X/Open extended v?fprintf implemented in terms of v?fprintf. Written by James Clark (jjc@jclark.com). */ /* Compile with: -DVARARGS to use varargs.h instead of stdarg.h -DLONG_DOUBLE_MISSING if your compiler doesn't like `long double' -DFP_SUPPORT to include floating point stuff */ #include "config.h" #ifndef HAVE_EXTENDED_PRINTF #include "std.h" #ifdef lint /* avoid stupid lint warnings */ #undef va_arg #define va_arg(ap, type) (ap, (type)0) #endif #ifdef FP_SUPPORT #ifdef LONG_DOUBLE_MISSING typedef double long_double; #else typedef long double long_double; #endif #endif /* FP_SUPPORT */ #ifndef __STDC__ #define const /* as nothing */ #endif #ifdef USE_PROTOTYPES #define P(parms) parms #else #define P(parms) () #endif #ifdef VARARGS typedef int (*printer)(); #else typedef int (*printer)(UNIV, const char *, ...); #endif enum arg_type { NONE, INT, UNSIGNED, LONG, UNSIGNED_LONG, #ifdef FP_SUPPORT DOUBLE, LONG_DOUBLE, #endif /* FP_SUPPORT */ PCHAR, PINT, PLONG, PSHORT }; union arg { int i; unsigned u; long l; unsigned long ul; #ifdef FP_SUPPORT double d; long_double ld; #endif /* FP_SUPPORT */ char *pc; UNIV pv; int *pi; short *ps; long *pl; }; #define NEXT 0 #define MISSING 10 struct spec { enum arg_type type; char pos; char field_width; char precision; }; #define FLAG_CHARS "-+ #0" static int parse_spec P((const char **, struct spec *)); static int find_arg_types P((const char *, enum arg_type *)); static void get_arg P((enum arg_type, va_list *, union arg *)); static int do_arg P((UNIV, printer, const char *, enum arg_type, union arg *)); static int xdoprt P((UNIV, printer, const char *, va_list)); static int printit P((UNIV, printer, const char *, va_list, int, union arg *)); static int maybe_positional P((const char *)); /* Return 1 if sucessful, 0 otherwise. **pp points to character after % */ static int parse_spec(pp, sp) const char **pp; struct spec *sp; { char modifier = 0; sp->pos = NEXT; if (isdigit((unsigned char)(**pp)) && (*pp)[1] == '$') { if (**pp == '0') return 0; sp->pos = **pp - '0'; *pp += 2; } while (**pp != '\0' && strchr(FLAG_CHARS, **pp)) *pp += 1; /* handle the field width */ sp->field_width = MISSING; if (**pp == '*') { *pp += 1; if (isdigit((unsigned char)**pp) && (*pp)[1] == '$') { if (**pp == '0') return 0; sp->field_width = **pp - '0'; *pp += 2; } else sp->field_width = NEXT; } else { while (isdigit((unsigned char)**pp)) *pp += 1; } /* handle the precision */ sp->precision = MISSING; if (**pp == '.') { *pp += 1; if (**pp == '*') { *pp += 1; if (isdigit((unsigned char)**pp) && (*pp)[1] == '$') { if (**pp == '0') return 0; sp->precision = **pp - '0'; *pp += 2; } else sp->precision = NEXT; } else { while (isdigit((unsigned char)**pp)) *pp += 1; } } /* handle h l or L */ if (**pp == 'h' || **pp == 'l' || **pp == 'L') { modifier = **pp; *pp += 1; } switch (**pp) { case 'd': case 'i': sp->type = modifier == 'l' ? LONG : INT; break; case 'o': case 'u': case 'x': case 'X': sp->type = modifier == 'l' ? UNSIGNED_LONG : UNSIGNED; break; #ifdef FP_SUPPORT case 'e': case 'E': case 'f': case 'g': case 'G': sp->type = modifier == 'L' ? LONG_DOUBLE : DOUBLE; break; #endif /* FP_SUPPORT */ case 'c': sp->type = INT; break; case 's': sp->type = PCHAR; break; case 'p': /* a pointer to void has the same representation as a pointer to char */ sp->type = PCHAR; break; case 'n': if (modifier == 'h') sp->type = PSHORT; else if (modifier == 'l') sp->type = PLONG; else sp->type = PINT; break; case '%': sp->type = NONE; break; default: return 0; } *pp += 1; return 1; } static int find_arg_types(format, arg_type) const char *format; enum arg_type *arg_type; { int i, pos; const char *p; struct spec spec; for (i = 0; i < 9; i++) arg_type[i] = NONE; pos = 0; p = format; while (*p) if (*p == '%') { p++; if (!parse_spec(&p, &spec)) return 0; if (spec.type != NONE) { int n; if (spec.pos == NEXT) n = pos++; else n = spec.pos - 1; if (n < 9) { enum arg_type t = arg_type[n]; if (t != NONE && t != spec.type) return 0; arg_type[n] = spec.type; } } if (spec.field_width != MISSING) { int n; if (spec.field_width == NEXT) n = pos++; else n = spec.field_width - 1; if (n < 9) { enum arg_type t = arg_type[n]; if (t != NONE && t != INT) return 0; arg_type[n] = INT; } } if (spec.precision != MISSING) { int n; if (spec.precision == NEXT) n = pos++; else n = spec.precision - 1; if (n < 9) { enum arg_type t = arg_type[n]; if (t != NONE && t != INT) return 0; arg_type[n] = INT; } } } else p++; return 1; } static void get_arg(arg_type, app, argp) enum arg_type arg_type; va_list *app; union arg *argp; { switch (arg_type) { case NONE: break; case INT: argp->i = va_arg(*app, int); break; case UNSIGNED: argp->u = va_arg(*app, unsigned); break; case LONG: argp->l = va_arg(*app, long); break; case UNSIGNED_LONG: argp->ul = va_arg(*app, unsigned long); break; #ifdef FP_SUPPORT case DOUBLE: argp->d = va_arg(*app, double); break; case LONG_DOUBLE: argp->ld = va_arg(*app, long_double); break; #endif /* FP_SUPPORT */ case PCHAR: argp->pc = va_arg(*app, char *); break; case PINT: argp->pi = va_arg(*app, int *); break; case PSHORT: argp->ps = va_arg(*app, short *); break; case PLONG: argp->pl = va_arg(*app, long *); break; default: abort(); } } static int do_arg(handle, func, buf, arg_type, argp) UNIV handle; printer func; const char *buf; enum arg_type arg_type; union arg *argp; { switch (arg_type) { case NONE: return (*func)(handle, buf); case INT: return (*func)(handle, buf, argp->i); case UNSIGNED: return (*func)(handle, buf, argp->u); case LONG: return (*func)(handle, buf, argp->l); case UNSIGNED_LONG: return (*func)(handle, buf, argp->ul); #ifdef FP_SUPPORT case DOUBLE: return (*func)(handle, buf, argp->d); case LONG_DOUBLE: return (*func)(handle, buf, argp->ld); #endif /* FP_SUPPORT */ case PCHAR: return (*func)(handle, buf, argp->pc); case PINT: return (*func)(handle, buf, argp->pi); case PSHORT: return (*func)(handle, buf, argp->ps); case PLONG: return (*func)(handle, buf, argp->pl); default: abort(); } /* NOTREACHED */ } static int printit(handle, func, p, ap, nargs, arg) UNIV handle; printer func; const char *p; va_list ap; int nargs; union arg *arg; { char buf[512]; /* enough for a spec */ int count = 0; int pos = 0; while (*p) if (*p == '%') { char *q; struct spec spec; const char *start; int had_field_width; union arg *argp; union arg a; int res; start = ++p; if (!parse_spec(&p, &spec)) abort(); /* should have caught it in find_arg_types */ buf[0] = '%'; q = buf + 1; if (spec.pos != NEXT) start += 2; /* substitute in precision and field width if necessary */ had_field_width = 0; while (start < p) { if (*start == '*') { char c; int n, val; start++; if (!had_field_width && spec.field_width != MISSING) { c = spec.field_width; had_field_width = 1; } else c = spec.precision; if (c == NEXT) n = pos++; else { start += 2; n = c - 1; } if (n >= nargs) val = va_arg(ap, int); else val = arg[n].i; /* ignore negative precision */ if (val >= 0 || q[-1] != '.') { (void)sprintf(q, "%d", val); q = strchr(q, '\0'); } } else *q++ = *start++; } *q++ = '\0'; argp = 0; if (spec.type != NONE) { int n = spec.pos == NEXT ? pos++ : spec.pos - 1; if (n >= nargs) { get_arg(spec.type, &ap, &a); argp = &a; } else argp = arg + n; } res = do_arg(handle, func, buf, spec.type, argp); if (res < 0) return -1; count += res; } else { if ((*func)(handle, "%c", *p++) < 0) return -1; count++; } return count; } /* Do a quick check to see if it may contains any positional thingies. */ static int maybe_positional(format) const char *format; { const char *p; p = format; for (;;) { p = strchr(p, '$'); if (!p) return 0; if (p - format >= 2 && isdigit((unsigned char)p[-1]) && (p[-2] == '%' || p[-2] == '*')) break; /* might be a positional thingy */ } return 1; } static int xdoprt(handle, func, format, ap) UNIV handle; printer func; const char *format; va_list ap; { enum arg_type arg_type[9]; union arg arg[9]; int nargs, i; if (!find_arg_types(format, arg_type)) return -1; for (nargs = 0; nargs < 9; nargs++) if (arg_type[nargs] == NONE) break; for (i = nargs; i < 9; i++) if (arg_type[i] != NONE) return -1; for (i = 0; i < nargs; i++) get_arg(arg_type[i], &ap, arg + i); return printit(handle, func, format, ap, nargs, arg); } #ifdef VARARGS static int do_fprintf(va_alist) va_dcl #else static int do_fprintf(UNIV p, const char *format,...) #endif { #ifdef VARARGS UNIV p; const char *format; #endif va_list ap; int res; #ifdef VARARGS va_start(ap); p = va_arg(ap, UNIV); format = va_arg(ap, char *); #else va_start(ap, format); #endif res = vfprintf((FILE *)p, format, ap); va_end(ap); return res; } #ifdef VARARGS int xfprintf(va_alist) va_dcl #else int xfprintf(FILE *fp, const char *format, ...) #endif { #ifdef VARARGS FILE *fp; char *format; #endif va_list ap; int res; #ifdef VARARGS va_start(ap); fp = va_arg(ap, FILE *); format = va_arg(ap, char *); #else va_start(ap, format); #endif if (maybe_positional(format)) res = xdoprt((UNIV)fp, do_fprintf, format, ap); else res = vfprintf(fp, format, ap); va_end(ap); return res; } int xvfprintf(fp, format, ap) FILE *fp; const char *format; va_list ap; { int res; if (maybe_positional(format)) res = xdoprt((UNIV)fp, do_fprintf, format, ap); else res = vfprintf(fp, format, ap); return res; } #endif /* not HAVE_EXTENDED_PRINTF */