aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'toys/pending/printf.c')
-rw-r--r--toys/pending/printf.c178
1 files changed, 70 insertions, 108 deletions
diff --git a/toys/pending/printf.c b/toys/pending/printf.c
index 3d1a73f2..e1c77c5b 100644
--- a/toys/pending/printf.c
+++ b/toys/pending/printf.c
@@ -20,12 +20,6 @@ config PRINTF
#define FOR_printf
#include "toys.h"
-GLOBALS(
- char *hv_w;
- char *hv_p;
- int encountered;
-)
-
// Detect matching character (return true/valse) and advance pointer if match.
static int eat(char **s, char c)
{
@@ -36,78 +30,19 @@ static int eat(char **s, char c)
return x;
}
-// Add ll and L to Interger and floating point formats respectively.
-static char *get_format(char *f)
-{
- int len = strlen(f);
- char last = f[--len], *post = "";
-
- f[len] = 0;
- if (strchr("diouxX", last)) post = "ll"; // add ll to integer modifier.
- else if (strchr("feEgG", last)) post = "L"; // add L to float modifier.
- return xmprintf("%s%s%c", f, post, last);
-}
-
-// Print arguments with corresponding conversion and width and precision.
-static void print(char *fmt, int w, int p, char *arg)
-{
- char *ptr = fmt, *ep = 0, *format = 0;
-
- errno = 0;
- if (strchr("diouxX", *ptr)) {
- long long val = 0;
-
- if (arg) {
- if (*arg == '\'' || *arg == '"') val = arg[1];
- else {
- val = strtoll(arg, &ep, 0);
- if (errno || (ep && (*ep || ep == arg))) {
- perror_msg("Invalid num %s", arg);
- val = 0;
- }
- }
- }
- format = get_format(fmt);
- TT.hv_w ? (TT.hv_p ? printf(format, w, p, val) : printf(format, w, val))
- : (TT.hv_p ? printf(format, p, val) : printf(format, val));
- } else if (strchr("gGeEf", *ptr)) {
- long double dval = 0;
-
- if (arg) {
- dval = strtold(arg, &ep);
- if (errno || (ep && (*ep || ep == arg))) {
- perror_msg("Invalid num %s", arg);
- dval = 0;
- }
- }
- format = get_format(fmt);
- TT.hv_w ? (TT.hv_p ? printf(format, w, p, dval) : printf(format, w, dval))
- : (TT.hv_p ? printf(format, p, dval) : printf(format, dval));
- } else if (*ptr == 's') {
- char *str = arg;
-
- if (!str) str = "";
-
- TT.hv_w ? (TT.hv_p ? printf(fmt,w,p,str): printf(fmt, w, str))
- : (TT.hv_p ? printf(fmt, p, str) : printf(fmt, str));
- } else if (*ptr == 'c') printf(fmt, arg ? *arg : 0);
-
- if (format) free(format);
-}
-
// Parse escape sequences.
static int handle_slash(char **esc_val)
{
char *ptr = *esc_val;
- int len = 1, base = 0;
- unsigned result = 0;
+ int len, base = 0;
+ unsigned result = 0, num;
if (*ptr == 'c') xexit();
// 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits.
if (eat(&ptr, 'x')) base = 16;
else if (*ptr >= '0' && *ptr <= '8') base = 8;
- len += (base-8)/8;
+ len = (char []){0,3,2}[base/8];
// Not a hex or octal escape? (This catches trailing \)
if (!len) {
@@ -118,9 +53,8 @@ static int handle_slash(char **esc_val)
}
while (len) {
- unsigned num = tolower(*ptr)-'0';
-
- if (num > 10) num += '0'-'a'+10;
+ num = tolower(*ptr) - '0';
+ if (num >= 'a'-'0') num += '0'-'a'+10;
if (num >= base) {
// Don't parse invalid hex value ala "\xvd", print it verbatim
if (base == 16 && len == 2) {
@@ -135,48 +69,76 @@ static int handle_slash(char **esc_val)
}
*esc_val = ptr;
- return (char)result;
+ return result;
}
void printf_main(void)
{
- char *format = *toys.optargs, **arg = toys.optargs+1, *f, *p;
-
- for (f = format; *f; f++) {
- if (eat(&f, '\\')) putchar(handle_slash(&f));
- else if (!eat(&f, '%') || *f == '%') putchar(*f);
- else if (*f == 'b')
- for (p = *arg ? *(arg++) : ""; *p; p++)
- putchar(eat(&p, '\\') ? handle_slash(&p) : *p);
- else {
- char *start = f;
- int wp[2], i;
-
- // todo: we currently ignore these?
- if (strchr("-+# ", *f)) f++;
- memset(wp, 0, 8);
- for (i=0; i<2; i++) {
- if (eat(&f, '*')) {
- if (*arg) wp[i] = atolx(*(arg++));
- } else while (isdigit(*f)) f++;
- if (!eat(&f, '.')) break;
- }
- if (!(p = strchr("diouxXfeEgGcs", *f)))
- error_exit("bad format@%ld", f-format);
+ char **arg = toys.optargs+1;
+
+ // Repeat format until arguments consumed
+ for (;;) {
+ int seen = 0;
+ char *f = *toys.optargs;
+
+ // Loop through characters in format
+ while (*f) {
+ if (eat(&f, '\\')) putchar(handle_slash(&f));
+ else if (!eat(&f, '%') || *f == '%') putchar(*f++);
+
+ // Handle %escape
else {
- int len = f-start;
-
- TT.hv_p = strstr(start, ".*");
- TT.hv_w = strchr(start, '*');
- //pitfall: handle diff b/w * and .*
- if ((TT.hv_w-1) == TT.hv_p) TT.hv_w = NULL;
- memcpy((p = xzalloc(len+1)), start, len);
- print(p+len-1, wp[0], wp[1], *arg);
- if (*arg) arg++;
- free(p);
- p = NULL;
- }
- TT.encountered = 1;
+ char c, *start = f, *end = 0, *aa, *width = "";
+ int wp[] = {-1,-1}, i;
+ union {
+ int c; // type promotion
+ char *str;
+ long long ll;
+ double dd;
+ } mash;
+
+ // Parse width.precision between % and type indicator.
+ // todo: we currently ignore these?
+ if (strchr("-+# ", *f)) f++;
+ for (i=0; i<2; i++) {
+ if (eat(&f, '*')) {
+ if (*arg) wp[i] = atolx(*arg++);
+ } else while (isdigit(*f)) f++;
+ if (!eat(&f, '.')) break;
+ }
+ seen++;
+ errno = 0;
+ c = *f++;
+ aa = *arg ? *arg++ : "";
+
+ // Handle %esc, assembling new format string into toybuf if necessary.
+ if ((f-start) > sizeof(toybuf)-4) c = 0;
+ if (c == 'b') {
+ while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa) : *aa++);
+
+ continue;
+ } else if (c == 'c') mash.c = *aa;
+ else if (c == 's') mash.str = aa;
+ else if (strchr("diouxX", c)) {
+ width = "ll";
+ if (*aa == '\'' || *aa == '"') mash.ll = aa[1];
+ else mash.ll = strtoll(aa, &end, 0);
+ } else if (strchr("feEgG", c)) mash.dd = strtod(aa, &end);
+ else error_exit("bad %%%c@%ld", c, f-*toys.optargs);
+
+ if (end && (errno || *end)) perror_msg("bad %%%c %s", c, aa);
+
+ sprintf(toybuf, "%%%.*s%s%c", (int)(f-start)-1, start, width, c);
+ wp[0]>=0
+ ? (wp[1]>=0 ? printf(toybuf, wp[0], wp[1], mash)
+ : printf(toybuf, wp[0], mash))
+ : (wp[1]>=0 ? printf(toybuf, wp[1], mash)
+ : printf(toybuf, mash));
+ }
}
+
+ // Posix says to keep looping through format until we consume all args.
+ // This only works if the format actually consumed at least one arg.
+ if (!seen || !*arg) break;
}
}