Support having multiple flags for a single conversion; at least '+'/'#' and
'0' are not exclusive.

Further use strspn() instead of inlined version for correct handling of
string ending with allowed characters.
---
 printf.c             | 37 ++++++++++++++++++++-----------------
 tests/0002-printf.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+), 17 deletions(-)
 create mode 100644 tests/0002-printf.sh

diff --git a/printf.c b/printf.c
index 039dac7..4d0399b 100644
--- a/printf.c
+++ b/printf.c
@@ -19,11 +19,11 @@ int
 main(int argc, char *argv[])
 {
        Rune *rarg;
-       size_t i, j, argi, lastargi, formatlen, blen;
+       size_t i, j, f, argi, lastargi, formatlen, blen, nflags;
        long long num;
        double dou;
        int cooldown = 0, width, precision, ret = 0;
-       char *format, *tmp, *arg, *fmt, flag;
+       char *format, *tmp, *arg, *fmt;
 
        argv0 = argv[0];
        if (argc < 2)
@@ -44,14 +44,20 @@ main(int argc, char *argv[])
                                break;
                        lastargi = argi;
                }
+
                if (format[i] != '%') {
                        putchar(format[i]);
                        continue;
                }
 
                /* flag */
-               for (flag = '\0', i++; strchr("#-+ 0", format[i]); i++) {
-                       flag = format[i];
+               f = ++i;
+               nflags = strspn(&format[f], "#-+ 0");
+               i += nflags;
+
+               if (nflags > INT_MAX) {
+                       f += nflags - INT_MAX;
+                       nflags = INT_MAX;
                }
 
                /* field width */
@@ -64,7 +70,7 @@ main(int argc, char *argv[])
                        i++;
                } else {
                        j = i;
-                       for (; strchr("+-0123456789", format[i]); i++);
+                       i += strspn(&format[i], "+-0123456789");
                        if (j != i) {
                                tmp = estrndup(format + j, i - j);
                                width = estrtonum(tmp, 0, INT_MAX);
@@ -85,7 +91,7 @@ main(int argc, char *argv[])
                                i++;
                        } else {
                                j = i;
-                               for (; strchr("+-0123456789", format[i]); i++);
+                               i += strspn(&format[i], "+-0123456789");
                                if (j != i) {
                                        tmp = estrndup(format + j, i - j);
                                        precision = estrtonum(tmp, 0, INT_MAX);
@@ -127,9 +133,8 @@ main(int argc, char *argv[])
                        free(rarg);
                        break;
                case 's':
-                       fmt = estrdup(flag ? "%#*.*s" : "%*.*s");
-                       if (flag)
-                               fmt[1] = flag;
+                       fmt = emalloc(sizeof("%*.*s" + nflags));
+                       sprintf(fmt, "%%%.*s*.*s", (int)nflags, &format[f]);
                        printf(fmt, width, precision, arg);
                        free(fmt);
                        break;
@@ -161,22 +166,20 @@ main(int argc, char *argv[])
                        } else {
                                        num = 0;
                        }
-                       fmt = estrdup(flag ? "%#*.*ll#" : "%*.*ll#");
-                       if (flag)
-                               fmt[1] = flag;
-                       fmt[flag ? 7 : 6] = format[i];
+                       fmt = emalloc(sizeof("%*.*ll#") + nflags);
+                       sprintf(fmt, "%%%.*s*.*ll%c", (int)nflags, &format[f], 
format[i]);
                        printf(fmt, width, precision, num);
                        free(fmt);
                        break;
                case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': 
case 'g': case 'G':
-                       fmt = estrdup(flag ? "%#*.*#" : "%*.*#");
-                       if (flag)
-                               fmt[1] = flag;
-                       fmt[flag ? 5 : 4] = format[i];
+                       fmt = emalloc(sizeof("%*.*#") + nflags);
+                       sprintf(fmt, "%%%.*s*.*%c", (int)nflags, &format[f], 
format[i]);
                        dou = (strlen(arg) > 0) ? estrtod(arg) : 0;
                        printf(fmt, width, precision, dou);
                        free(fmt);
                        break;
+               case '\0':
+                       eprintf("Missing format specifier.\n");
                default:
                        eprintf("Invalid format specifier '%c'.\n", format[i]);
                }
diff --git a/tests/0002-printf.sh b/tests/0002-printf.sh
new file mode 100644
index 0000000..6384fe2
--- /dev/null
+++ b/tests/0002-printf.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+set -e
+
+exp1=exp1.$$
+exp2=exp2.$$
+res1=res1.$$
+res2=res2.$$
+
+cleanup()
+{
+       st=$?
+       rm -f $exp1 $exp2 $res1 $res2
+       exit $st
+}
+
+trap cleanup EXIT HUP INT TERM
+
+cat <<'EOF' > $exp1
+123
+0
+foo
+bar
++001   +2 +003 -400 
+Expected failure
+EOF
+
+cat <<'EOF' > $exp2
+../printf: Missing format specifier.
+EOF
+
+(
+       ../printf '123\n'
+       ../printf '%d\n'
+       ../printf '%b' 'foo\nbar\n'
+
+       # Two flags used simulatenously, + and 0
+       ../printf '%+04d %+4d ' 1 2 3 -400; ../printf "\n"
+       # Missing format specifier; should have sane error message
+       ../printf '%000' FOO || echo "Expected failure"
+) > $res1 2> $res2
+
+diff -u $exp1 $res1
+diff -u $exp2 $res2
-- 
2.42.0


Reply via email to