int printfcmd(int argc, char *argv[]) { char *fmt; char *format; int ch; rval = 0; nextopt(nullstr); argv = argptr; format = *argv; if (!format) { warnx("usage: printf format [arg ...]"); goto err; } gargv = ++argv; #define SKIP1 "#-+ 0" #define SKIP2 "*0123456789" do { /* * Basic algorithm is to scan the format string for conversion * specifications -- once one is found, find out if the field * width or precision is a '*'; if it is, gather up value. * Note, format strings are reused as necessary to use up the * provided arguments, arguments of zero/null string are * provided to use up the format string. */ /* find next format specification */ for (fmt = format; (ch = *fmt++) ;) { char *start; char nextch; int array[2]; int *param; if (ch == '\\') { int c_ch; fmt = conv_escape(fmt, &c_ch); ch = c_ch; goto pc; } if (ch != '%' || (*fmt == '%' && (++fmt || 1))) { pc: blt_putchar(ch); continue; } /* Ok - we've found a format specification, Save its address for a later blt_printf(). */ start = fmt - 1; param = array; /* skip to field width */ fmt += strspn(fmt, SKIP1); if (*fmt == '*') *param++ = getintmax(); /* skip to possible '.', get following precision */ fmt += strspn(fmt, SKIP2); if (*fmt == '.') ++fmt; if (*fmt == '*') *param++ = getintmax(); fmt += strspn(fmt, SKIP2); ch = *fmt; if (!ch) { warnx("missing format character"); goto err; } /* null terminate format string to we can use it as an argument to printf. */ nextch = fmt[1]; fmt[1] = 0; switch (ch) { case 'b': { int done = conv_escape_str(getstr()); char *p = stackblock(); *fmt = 's'; PF(start, p); /* escape if a \c was encountered */ if (done) goto out; *fmt = 'b'; break; } case 'c': { int p = getchr(); PF(start, p); break; } case 's': { char *p = getstr(); PF(start, p); break; } case 'd': case 'i': { intmax_t p = getintmax(); char *f = mklong(start, fmt); PF(f, p); break; } case 'o': case 'u': case 'x': case 'X': { uintmax_t p = getuintmax(); char *f = mklong(start, fmt); PF(f, p); break; } case 'e': case 'E': case 'f': case 'g': case 'G': { double p = getdouble(); PF(start, p); break; } default: warnx("%s: invalid directive", start); goto err; } *++fmt = nextch; } } while (gargv != argv && *gargv); out: return rval; err: return 1; }
int main(int argc, char *argv[]) { char *fmt, *start; int fieldwidth, precision; char nextch; char *format; int ch; int error; #if !defined(SHELL) && !defined(BUILTIN) (void)setlocale (LC_ALL, ""); #endif while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { case '?': default: usage(); return 1; } } argc -= optind; argv += optind; if (argc < 1) { usage(); return 1; } format = *argv; gargv = ++argv; #define SKIP1 "#-+ 0" #define SKIP2 "0123456789" do { /* * Basic algorithm is to scan the format string for conversion * specifications -- once one is found, find out if the field * width or precision is a '*'; if it is, gather up value. * Note, format strings are reused as necessary to use up the * provided arguments, arguments of zero/null string are * provided to use up the format string. */ /* find next format specification */ for (fmt = format; (ch = *fmt++) != '\0';) { if (ch == '\\') { char c_ch; fmt = conv_escape(fmt, &c_ch); putchar(c_ch); continue; } if (ch != '%' || (*fmt == '%' && ++fmt)) { (void)putchar(ch); continue; } /* Ok - we've found a format specification, Save its address for a later printf(). */ start = fmt - 1; /* skip to field width */ fmt += strspn(fmt, SKIP1); if (*fmt == '*') { fmt++; fieldwidth = getwidth(); } else fieldwidth = -1; /* skip to possible '.', get following precision */ fmt += strspn(fmt, SKIP2); if (*fmt == '.') { fmt++; if (*fmt == '*') { fmt++; precision = getwidth(); } else precision = -1; } else precision = -1; fmt += strspn(fmt, SKIP2); ch = *fmt; if (!ch) { warnx("missing format character"); return (1); } /* null terminate format string to we can use it as an argument to printf. */ nextch = fmt[1]; fmt[1] = 0; switch (ch) { case 'B': { const char *p = conv_expand(getstr()); if (p == NULL) goto out; *fmt = 's'; PF(start, p); if (error < 0) goto out; break; } case 'b': { /* There has to be a better way to do this, * but the string we generate might have * embedded nulls. */ static char *a, *t; char *cp = getstr(); /* Free on entry in case shell longjumped out */ if (a != NULL) free(a); a = NULL; if (t != NULL) free(t); t = NULL; /* Count number of bytes we want to output */ b_length = 0; conv_escape_str(cp, b_count); t = malloc(b_length + 1); if (t == NULL) goto out; (void)memset(t, 'x', b_length); t[b_length] = 0; /* Get printf to calculate the lengths */ *fmt = 's'; abort(); /* APF(&a, start, t); */ if (error == -1) goto out; b_fmt = a; /* Output leading spaces and data bytes */ conv_escape_str(cp, b_output); /* Add any trailing spaces */ printf("%s", b_fmt); break; } case 'c': { char p = getchr(); PF(start, p); if (error < 0) goto out; break; } case 's': { char *p = getstr(); PF(start, p); if (error < 0) goto out; break; } case 'd': case 'i': { intmax_t p = getintmax(); char *f = mklong(start, ch); PF(f, p); if (error < 0) goto out; break; } case 'o': case 'u': case 'x': case 'X': { uintmax_t p = getuintmax(); char *f = mklong(start, ch); PF(f, p); if (error < 0) goto out; break; } case 'e': case 'E': case 'f': case 'g': case 'G': { double p = getdouble(); PF(start, p); if (error < 0) goto out; break; } default: warnx("%s: invalid directive", start); return 1; } *fmt++ = ch; *fmt = nextch; /* escape if a \c was encountered */ if (rval & 0x100) return rval & ~0x100; } } while (gargv != argv && *gargv); return rval & ~0x100; out: warn("print failed"); return 1; }