[PATCH] awk: add '*' as a valid specifier for width and precision in printf

Anubhav Kokane via busybox <[email protected]> Thu, 25 Jun 2026 11:38:59 +0000
Newsgroups gmane.linux.busybox
Message-ID <[email protected]>
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.

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