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"
>