logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/
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:

Mcmd/printf.c156++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mtest-cmd/printf.sh11++++++++++-
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