From 4ecd980caee4ba3903207de7b2e3993393dff173 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Fri, 31 Jul 2026 17:29:31 -0500 Subject: [PATCH] Replace anystart() working on an array with anystrz() working on a string with embedded NUL bytes. And tell compiler that assigning a const pointer into a non-const pointer isn't an error ("string constants" have been in the rodata segment forever, it segfaults if we write, it's sad the optimizer may not have quite as much information which is not my problem). This eliminates several gratuitous (void *)typecasts, but several more can go in the rest of the tree. --- lib/lib.c | 10 ---------- lib/lib.h | 1 - scripts/portability.sh | 4 ++-- toys/pending/sh.c | 29 ++++++++++++++++++++--------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/lib.c b/lib/lib.c index 7ae3ff66..4b69f05a 100644 --- a/lib/lib.c +++ b/lib/lib.c @@ -523,16 +523,6 @@ int strcasestart(char **a, char *b) return i; } -// return length of match found at this point (try is null terminated array) -int anystart(char *s, char **try) -{ - char *ss = s; - - while (*try) if (strstart(&s, *try++)) return s-ss; - - return 0; -} - // does this entire string match one of the strings in try[]? // Returns 0 if not, index+1 if so int anystr(char *s, char **try) diff --git a/lib/lib.h b/lib/lib.h index 0fc9cbe4..d24a65e9 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -229,7 +229,6 @@ int unescape2(char **c, int echo); char *strend(char *str, char *suffix); int strstart(char **a, char *b); int strcasestart(char **a, char *b); -int anystart(char *s, char **try); int anystr(char *s, char **try); int same_file(struct stat *st1, struct stat *st2); int same_dev_ino(struct stat *st, struct dev_ino *di); diff --git a/scripts/portability.sh b/scripts/portability.sh index 570d9e29..e72c644c 100644 --- a/scripts/portability.sh +++ b/scripts/portability.sh @@ -24,9 +24,9 @@ fi # Disable pointless warnings only clang produces [ -n "$("$CROSS_COMPILE$CC" --version | grep -w clang)" ] && - CFLAGS+=" -Wno-string-plus-int -Wno-invalid-source-encoding" || + CFLAGS+=" -Wno-string-plus-int -Wno-invalid-source-encoding -Wno-incompatible-pointer-types-discards-qualifiers" || # And ones only gcc produces - CFLAGS+=" -Wno-restrict -Wno-format-overflow" + CFLAGS+=" -Wno-restrict -Wno-format-overflow -Wno-discarded-qualifiers" # Address Sanitizer if [ -n "$ASAN" ]; then diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 51a9db84..1c3de708 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -451,6 +451,18 @@ GLOBALS( struct sh_arg jobs, *wcdeck; ) +// Return length of string found in concatenated list of null terminated +// strings (ending with \0\0), or 0 if not found. +static int anystrz(char *find, char *list) +{ + int len; + + for (;(len = strlen(list)); list += len+1) + if (strstart(&find, list)) return len; + + return 0; +} + #define DEBUG 0 static void debug_show_fds(char *who) @@ -480,8 +492,8 @@ static struct sh_vars *setvar(char *str); // ordered for greedy matching, so >&; becomes >& ; not > &; // making these const means I need to typecast the const away later to // avoid endless warnings. -static const char *redirectors[] = {"<<<", "<<-", "<<", "<&", "<>", "<", ">>", - ">&", ">|", ">", "&>>", "&>", 0}; +static const char *redirectors = "<<<\0<<-\0<<\0<&\0<>\0<\0>>\0>&\0>|\0>\0&>>\0" + "&>\0"; // The order of these has to match the string in set_main() #define OPT_B 0x100 @@ -1176,7 +1188,7 @@ static char *parse_word(char *start, int early) if (strstart(&ss, "<(") || strstart(&ss, ">(")) { toybuf[quote++]=')'; end = ss; - } else if ((ii = anystart(ss, (void *)redirectors))) return ss+ii; + } else if ((ii = anystrz(ss, redirectors))) return ss+ii; if (strstart(&end, "((")) toybuf[quote++] = 254; // Loop to find end of this word @@ -1209,9 +1221,8 @@ static char *parse_word(char *start, int early) // space and flow control chars only end word when not quoted in any way } else { if (isspace(*end)) break; - ss = end + anystart(end, (char *[]){";;&", ";;", ";&", ";", "||", - "|&", "|", "&&", "&", "(", ")", 0}); - if (ss==end) ss += anystart(end, (void *)redirectors); + ss = end + anystrz(end, ";;&\0;;\0;&\0;\0||\0|&\0|\0&&\0&\0(\0)\0"); + if (ss==end) ss += anystrz(end, redirectors); if (ss!=end) return (end==start) ? ss : end; } @@ -2656,7 +2667,7 @@ static int expand_redir(struct sh_process *pp, struct sh_arg *arg, int skip) // Is this a redirect? s = prefix, ss = operator ss = skip_redir_prefix(s); - sss = ss + anystart(ss, (void *)redirectors); + sss = ss + anystrz(ss, redirectors); if (ss == sss) { // Nope: save/expand argument and loop if (expand_arg(&pp->arg, s, 0, &pp->delete)) goto qfail; @@ -2958,7 +2969,7 @@ static struct sh_process *run_command(int local) // Collect leading redirects and prefix assignments if (!skiplen) for (; iic && !pp->exit; ii++) { // Need to use original arg for <c and provide skip - if (anystart(skip_redir_prefix(s = arg->v[ii]), (void *)redirectors)) { + if (anystrz(skip_redir_prefix(s = arg->v[ii]), redirectors)) { if ((skiplen = ii)<(jj = arg->c)) ii++; arg->c = ii+1; // TODO should expand_redir() understand 1-skiplen to avoid arg->c swap? @@ -3340,7 +3351,7 @@ if (DEBUG) dprintf(2, "%d %p(%d) %s word=%.*s\n", getpid(), pl, pl ? pl->type : // ! x=y and xnoalias = -2; start = 0; } -- 2.39.5