commit: 909adecb75385b77e2bac89d110872e7011a027a
parent 36fb74d7d645b4708143d71c0fe6b034582ff830
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Tue, 24 Jun 2025 21:56:05 +0200
cmd/printf: no modulo on getting arguments
Diffstat:
2 files changed, 116 insertions(+), 51 deletions(-)
diff --git a/cmd/printf.c b/cmd/printf.c
@@ -9,6 +9,9 @@
#include <stdlib.h> // strtoul, strtod
#include <string.h> // strlen, memchr
+#undef MAX
+#define MAX(a, b) (a > b) ? a : b
+
// [1-9]
static int
isndigit(int c)
@@ -198,7 +201,6 @@ main(int argc, char *argv[])
// To keep argv intact for '%n$' format conversion specifiers
char **fmt_argv = argv;
- unsigned int fmt_argn = 0;
if(!strchr(fmt, '%'))
{
@@ -208,6 +210,8 @@ main(int argc, char *argv[])
do
{
+ unsigned int fmt_argn = 0;
+
for(char *fmt_idx = fmt; fmt_idx < (fmt + fmtlen); fmt_idx++)
{
// Field width provided for consistency with C printf
@@ -243,9 +247,19 @@ main(int argc, char *argv[])
unsigned int num = strtoul(fmt_idx, &num_end, 10);
if(errno == 0 && num != 0 && num_end && *num_end == '$')
{
- fmt_arg = argv[(num - 1) % argc];
+ if(num > argc)
+ {
+ fprintf(stderr,
+ "printf: error: (format position %d) argument '%d' does not exists, only got "
+ "%d arguments\n",
+ (int)(fmt_idx - fmt),
+ num,
+ argc);
+ return 1;
+ }
+ fmt_argn = MAX(fmt_argn, num);
+ fmt_arg = fmt_argv[num - 1];
fmt_idx = num_end + 1;
- fmt_buf[fmt_bufi++] = '*';
}
}
@@ -256,7 +270,7 @@ main(int argc, char *argv[])
if(memchr(fmt_buf, *fmt_idx, fmt_bufi + 1))
{
fprintf(stderr,
- "fprintf: error: (format position %d) flag '%c' already set\n",
+ "printf: error: (format position %d) flag '%c' already set\n",
(int)(fmt_idx - fmt),
*fmt_idx);
return 1;
@@ -273,7 +287,7 @@ main(int argc, char *argv[])
if(argc <= 0)
{
fprintf(stderr,
- "fprintf: error: (format position %d) field-width argument without format "
+ "printf: error: (format position %d) field-width argument without format "
"arguments\n",
(int)(fmt_idx - fmt));
return 1;
@@ -312,25 +326,39 @@ main(int argc, char *argv[])
(int)(fmt_idx - fmt));
return 1;
}
+ if(num > argc)
+ {
+ fprintf(stderr,
+ "printf: error: (format position %d) argument '%d' does not exists, only got "
+ "%d arguments\n",
+ (int)(fmt_idx - fmt),
+ num,
+ argc);
+ return 1;
+ }
- fwidth_arg = fmt_argv[(num - 1) % argc];
+ fmt_argn = MAX(fmt_argn, num);
+ fwidth_arg = fmt_argv[num - 1];
fmt_idx = num_end + 1;
}
else
{
- fmt_arg = fmt_argv[fmt_argn++ % argc];
- fwidth_arg = fmt_argv[fmt_argn++ % argc];
+ if(fmt_argn < argc) fwidth_arg = fmt_argv[fmt_argn++];
+ if(fmt_argn < argc) fmt_arg = fmt_argv[fmt_argn++];
}
- errno = 0;
- fwidth = strtoul(fwidth_arg, NULL, 0);
- if(errno != 0)
+ if(fwidth_arg)
{
- fprintf(stderr,
- "printf: error: Failed parsing argument (%s) as a number for field width: %s\n",
- fwidth_arg,
- strerror(errno));
- return 1;
+ errno = 0;
+ fwidth = strtoul(fwidth_arg, NULL, 0);
+ if(errno != 0)
+ {
+ fprintf(stderr,
+ "printf: error: Failed parsing argument (%s) as a number for field width: %s\n",
+ fwidth_arg,
+ strerror(errno));
+ return 1;
+ }
}
}
else if(isdigit(fmt_idx[0]))
@@ -380,14 +408,13 @@ main(int argc, char *argv[])
if(argc <= 0)
{
fprintf(stderr,
- "fprintf: error: (format position %d) precision argument without format "
+ "printf: error: (format position %d) precision argument without format "
"arguments\n",
(int)(fmt_idx - fmt));
return 1;
}
char *prec_arg = NULL;
- fmt_idx++;
if(isndigit(*fmt_idx))
{
if(!fmt_arg)
@@ -419,25 +446,38 @@ main(int argc, char *argv[])
(int)(fmt_idx - fmt));
return 1;
}
+ if(num > argc)
+ {
+ fprintf(stderr,
+ "printf: error: (format position %d) argument '%d' does not exists, only got "
+ "%d arguments\n",
+ (int)(fmt_idx - fmt),
+ num,
+ argc);
+ return 1;
+ }
- prec_arg = fmt_argv[(num - 1) % argc];
-
+ fmt_argn = MAX(fmt_argn, num);
+ prec_arg = fmt_argv[num - 1];
fmt_idx = num_end + 1;
}
else
{
- prec_arg = fmt_argv[fmt_argn++ % argc];
+ if(fmt_argn < argc) prec_arg = fmt_argv[fmt_argn++];
}
- errno = 0;
- precision = strtoul(prec_arg, NULL, 0);
- if(errno != 0)
+ if(prec_arg)
{
- fprintf(stderr,
- "printf: error: Failed parsing argument (%s) as a number for precision: %s\n",
- prec_arg,
- strerror(errno));
- return 1;
+ errno = 0;
+ precision = strtoul(prec_arg, NULL, 0);
+ if(errno != 0)
+ {
+ fprintf(stderr,
+ "printf: error: Failed parsing argument (%s) as a number for precision: %s\n",
+ prec_arg,
+ strerror(errno));
+ return 1;
+ }
}
}
else if(isdigit(fmt_idx[0]))
@@ -488,7 +528,7 @@ main(int argc, char *argv[])
fmt_buf[fmt_bufi++] = *fmt_idx;
fmt_buf[fmt_bufi++] = '\0';
- if(!fmt_arg) fmt_arg = (argc == 0) ? (char *)"" : fmt_argv[fmt_argn++ % argc];
+ if(!fmt_arg && fmt_argn < argc) fmt_arg = fmt_argv[fmt_argn++];
switch(*fmt_idx)
{
@@ -497,10 +537,12 @@ main(int argc, char *argv[])
break;
/* strings */
case 's':
+ if(!fmt_arg) fmt_arg = (char *)"";
printf(fmt_buf, fwidth, precision, fmt_arg);
break;
case 'b':
{
+ if(!fmt_arg) fmt_arg = (char *)"";
size_t arglen = strlen(fmt_arg);
int clear = unescape(fmt_arg, &arglen, 0);
@@ -529,6 +571,7 @@ main(int argc, char *argv[])
}
case 'q':
{
+ if(!fmt_arg) fmt_arg = (char *)"";
if(fwidth != 0)
{
fprintf(
@@ -582,6 +625,7 @@ main(int argc, char *argv[])
break;
}
case 'c':
+ if(!fmt_arg) fmt_arg = (char *)"";
printf("%*c", fwidth, *fmt_arg);
break;
/* integers */
@@ -592,17 +636,21 @@ main(int argc, char *argv[])
case 'x':
case 'X':
{
- errno = 0;
- unsigned long int num = strtoul(fmt_arg, NULL, 0);
- if(errno != 0)
+ unsigned long int num = 0;
+ if(fmt_arg)
{
- fprintf(stderr,
- "printf: error: Failed parsing argument (%s) as a number for format conversion "
- "'%%%c': %s\n",
- fmt_arg,
- *fmt_idx,
- strerror(errno));
- return 1;
+ errno = 0;
+ num = strtoul(fmt_arg, NULL, 0);
+ if(errno != 0)
+ {
+ fprintf(stderr,
+ "printf: error: Failed parsing argument (%s) as a number for format conversion "
+ "'%%%c': %s\n",
+ fmt_arg,
+ *fmt_idx,
+ strerror(errno));
+ return 1;
+ }
}
printf(fmt_buf, fwidth, precision, num);
@@ -618,17 +666,22 @@ main(int argc, char *argv[])
case 'g':
case 'G':
{
- double num = strtod(fmt_arg, NULL);
- if(errno != 0)
+ double num = 0;
+ if(fmt_arg)
{
- fprintf(stderr,
- "printf: error: Failed parsing argument (%s) as a number for format conversion "
- "'%%%c': %s\n",
- fmt_arg,
- *fmt_idx,
- strerror(errno));
- return 1;
+ num = strtod(fmt_arg, NULL);
+ if(errno != 0)
+ {
+ fprintf(stderr,
+ "printf: error: Failed parsing argument (%s) as a number for format conversion "
+ "'%%%c': %s\n",
+ fmt_arg,
+ *fmt_idx,
+ strerror(errno));
+ return 1;
+ }
}
+
printf(fmt_buf, fwidth, precision, num);
break;
}
@@ -637,5 +690,8 @@ main(int argc, char *argv[])
return 1;
}
}
- } while(fmt_argn < argc);
+
+ fmt_argv += fmt_argn;
+ argc -= fmt_argn;
+ } while(argc > 0);
}
diff --git a/test-cmd/printf.sh b/test-cmd/printf.sh
@@ -2,7 +2,7 @@
# SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
# SPDX-License-Identifier: MPL-2.0
-plans=37
+plans=42
WD="$(dirname "$0")/../"
target="${WD}/cmd/printf"
. "${WD}/test-cmd/tap.sh"
@@ -69,3 +69,12 @@ t_args precision_s 'abcde' '%.5s' abcdefghijklmnopqrstuvwxyz
t_args nofmt_doubledash '--' '--'
t_args doubledash_fmt '10.000000' '--' '%f' 10
+
+t_args extraconv_s 'foo;bar;;' '%s;%s;%s;' foo bar
+t_args extraconv_d '42;69;0;' '%d;%d;%d;' 42 69
+
+# t_args --exit=1 extraconv_pos '' '%1$s'
+
+t_args fmt_star_width ' foo;bar;' '%*s;' 4 foo 2 bar
+t_args fmt_star_width_prec0 ' ; ;' '%*.0s;' 4 foo 2 bar
+t_args fmt_star_prec 'foo;ba;' '%.*s;' 4 foo 2 bar