Re: [PATCH] awk: add '*' as a valid specifier for width and precision in printf
David Laight via busybox <[email protected]> Fri, 26 Jun 2026 08:02:57 +0100
| Newsgroups | gmane.linux.busybox |
|---|---|
| Message-ID | <20260626080257.663a4506@pumpkin> |
On Thu, 25 Jun 2026 11:38:59 +0000 Anubhav Kokane via busybox <[email protected]> wrote: > Implemented by replacing '*' with the actual values of width and > precision from the argument list of awk printf in a dynamically > allocated string. This updated format string is passed to the > calls of xasprintf. There has to be a better way of doing it. And one that treats "%*4s" as an error. David > > Signed-off-by: Anubhav Kokane <[email protected]> > --- > editors/awk.c | 32 ++++++++++++++++++++++++++++++-- > testsuite/awk.tests | 22 ++++++++++++++++++++++ > 2 files changed, 52 insertions(+), 2 deletions(-) > > diff --git a/editors/awk.c b/editors/awk.c > index dd8f4ac42..07dff234a 100644 > --- a/editors/awk.c > +++ b/editors/awk.c > @@ -2423,6 +2423,7 @@ static char *awk_printf(node *n, size_t *len) > char *b; > char *fmt, *f; > size_t i; > + char *allocated; > > //tmpvar = nvalloc(1); > #define TMPVAR (&G.awk_printf__tmpvar) > @@ -2437,6 +2438,7 @@ static char *awk_printf(node *n, size_t *len) > > b = NULL; > i = 0; > + allocated = NULL; > while (1) { /* "print one format spec" loop */ > char *s; > char c; > @@ -2467,8 +2469,33 @@ static char *awk_printf(node *n, size_t *len) > while (1) { > if (isalpha(c)) > break; > - if (c == '*') /* gawk supports %*d and %*.*f, we don't... */ > - syntax_error("%*x formats are not supported"); > + if (c == '*') { > + char *temp; > + char num_s[12]; > + int num; > + size_t prefix_len; > + size_t suffix_len; > + size_t num_len; > + > + num = (int)getvar_i(evaluate(nextarg(&n), TMPVAR)); > + sprintf(num_s, "%d", num); > + > + num_len = strlen(num_s); > + prefix_len = f - s; > + suffix_len = strlen(f + 1); > + > + temp = xmalloc(prefix_len + suffix_len + num_len + 1); > + memcpy(temp, s, prefix_len); > + memcpy(temp + prefix_len, num_s, num_len); > + strcpy(temp + prefix_len + num_len, f + 1); > + > + if (allocated) > + free(allocated); > + > + s = temp; > + allocated = s; > + f = s + prefix_len + num_len - 1; > + } > c = *++f; > if (!c) { /* "....%...." and no letter found after % */ > /* Example: awk 'BEGIN { printf "^^^%^^^\n"; }' */ > @@ -2533,6 +2560,7 @@ static char *awk_printf(node *n, size_t *len) > } > > free(fmt); > + free(allocated); > //nvfree(tmpvar, 1); > #undef TMPVAR > > diff --git a/testsuite/awk.tests b/testsuite/awk.tests > index df1078bdb..76eb0f920 100755 > --- a/testsuite/awk.tests > +++ b/testsuite/awk.tests > @@ -24,6 +24,28 @@ testing "awk if operator >= " "awk 'BEGIN{if(23>=23) print \"foo\"}'" "foo\n" " > testing "awk if operator < " "awk 'BEGIN{if(2 < 13) print \"foo\"}'" "foo\n" "" "" > testing "awk if string == " "awk 'BEGIN{if(\"a\"==\"ab\") print \"bar\"}'" "" "" "" > > +# width and precision > +testing "awk only width" \ > +"awk 'BEGIN { printf \"%*d\", 10, -42 }'" " -42" "" "" > + > +testing "awk negative width" \ > +"awk 'BEGIN { printf \"%*s\", -10, \"hello\" }'" "hello " "" "" > + > +testing "awk only precision" \ > +"awk 'BEGIN { printf \"%.*f\n\", 2, 3.14159 }'" "3.14\n" "" "" > + > +testing "awk width and precision" \ > +"awk 'BEGIN { printf \"%*.*f\", 10, 2, 3.14159 }'" " 3.14" "" "" > + > +testing "awk 0 padding with width and precision" \ > +"awk 'BEGIN { printf \"%0*.*f\", 10, 2, 3.14 }'" "0000003.14" "" "" > + > +testing "awk multiple format specifications" \ > +"awk 'BEGIN { printf \"%d %*d %d\", 1, 5, 2, 3 }'" "1 2 3" "" "" > + > +testing "awk width with char" \ > +"awk 'BEGIN { printf \"%*c\", 5, 65 }'" " A" "" "" > + > # 4294967295 = 0xffffffff > testing "awk bitwise op" "awk '{ print or(4294967295,1) }'" "4294967295\n" "" "\n" >