From c2ce888030b6b880b3229520a8ff7d7026248a9a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Feb 2020 10:15:35 +0100 Subject: ash: parser: Only accept single-digit parameter expansion outside of braces Upstream commit: Date: Mon, 27 May 2019 13:39:37 +0800 parser: Only accept single-digit parameter expansion outside of braces This patch should fix the problem. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 2 +- shell/ash_test/ash-vars/var_10.right | 3 +++ shell/ash_test/ash-vars/var_10.tests | 4 ++++ shell/hush_test/hush-vars/var_10.right | 3 +++ shell/hush_test/hush-vars/var_10.tests | 4 ++++ 5 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 shell/ash_test/ash-vars/var_10.right create mode 100755 shell/ash_test/ash-vars/var_10.tests create mode 100644 shell/hush_test/hush-vars/var_10.right create mode 100755 shell/hush_test/hush-vars/var_10.tests diff --git a/shell/ash.c b/shell/ash.c index a25d14de0..90496ba7e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12602,7 +12602,7 @@ parsesub: { do { STPUTC(c, out); c = pgetc_eatbnl(); - } while (isdigit(c)); + } while (!subtype && isdigit(c)); } else if (c != '}') { /* $[{[#]][}] */ int cc = c; diff --git a/shell/ash_test/ash-vars/var_10.right b/shell/ash_test/ash-vars/var_10.right new file mode 100644 index 000000000..675ab45f6 --- /dev/null +++ b/shell/ash_test/ash-vars/var_10.right @@ -0,0 +1,3 @@ +Zero:0 +One:1 +Done:0 diff --git a/shell/ash_test/ash-vars/var_10.tests b/shell/ash_test/ash-vars/var_10.tests new file mode 100755 index 000000000..7364efb55 --- /dev/null +++ b/shell/ash_test/ash-vars/var_10.tests @@ -0,0 +1,4 @@ +set -- : 2 3 4 5 6 7 8 9 ten eleven +echo Zero$10 +echo One$11 +echo Done:$? diff --git a/shell/hush_test/hush-vars/var_10.right b/shell/hush_test/hush-vars/var_10.right new file mode 100644 index 000000000..675ab45f6 --- /dev/null +++ b/shell/hush_test/hush-vars/var_10.right @@ -0,0 +1,3 @@ +Zero:0 +One:1 +Done:0 diff --git a/shell/hush_test/hush-vars/var_10.tests b/shell/hush_test/hush-vars/var_10.tests new file mode 100755 index 000000000..7364efb55 --- /dev/null +++ b/shell/hush_test/hush-vars/var_10.tests @@ -0,0 +1,4 @@ +set -- : 2 3 4 5 6 7 8 9 ten eleven +echo Zero$10 +echo One$11 +echo Done:$? -- cgit v1.2.3-55-g6feb From 9ee5892798be81f7a6f3e070ecd52cbf0d55740e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Feb 2020 10:24:32 +0100 Subject: ash: expand: Fix trailing newlines processing in backquote expanding Upstream commit: Date: Mon, 29 Apr 2019 19:13:37 +0500 expand: Fix trailing newlines processing in backquote expanding According to POSIX.1-2008 we should remove newlines only at the end of the substitution. Newlines-only substitions causes dash to remove newlines before beggining of the substitution. The following code: cat <2" instead of expected "12". This patch fixes trailing newlines processing in backquote expanding. Signed-off-by: Nikolai Merinov Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 2 +- shell/ash_test/ash-psubst/tick_in_heredoc.right | 5 +++++ shell/ash_test/ash-psubst/tick_in_heredoc.tests | 7 +++++++ shell/hush_test/hush-psubst/tick_in_heredoc.right | 5 +++++ shell/hush_test/hush-psubst/tick_in_heredoc.tests | 7 +++++++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 shell/ash_test/ash-psubst/tick_in_heredoc.right create mode 100755 shell/ash_test/ash-psubst/tick_in_heredoc.tests create mode 100644 shell/hush_test/hush-psubst/tick_in_heredoc.right create mode 100755 shell/hush_test/hush-psubst/tick_in_heredoc.tests diff --git a/shell/ash.c b/shell/ash.c index 90496ba7e..dfe6d1c48 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6578,7 +6578,7 @@ expbackq(union node *cmd, int flag) /* Eat all trailing newlines */ dest = expdest; - for (; dest > (char *)stackblock() && dest[-1] == '\n';) + for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) STUNPUTC(dest); expdest = dest; diff --git a/shell/ash_test/ash-psubst/tick_in_heredoc.right b/shell/ash_test/ash-psubst/tick_in_heredoc.right new file mode 100644 index 000000000..7e7bac6d3 --- /dev/null +++ b/shell/ash_test/ash-psubst/tick_in_heredoc.right @@ -0,0 +1,5 @@ +1 + +2 + +3 diff --git a/shell/ash_test/ash-psubst/tick_in_heredoc.tests b/shell/ash_test/ash-psubst/tick_in_heredoc.tests new file mode 100755 index 000000000..c8eb8f4f4 --- /dev/null +++ b/shell/ash_test/ash-psubst/tick_in_heredoc.tests @@ -0,0 +1,7 @@ +cat < Date: Mon, 17 Feb 2020 11:22:59 +0100 Subject: ash: mkinit: Split reset into exitreset and reset Upstream commit: Date: Sat, 19 May 2018 02:39:40 +0800 mkinit: Split reset into exitreset and reset Previously reset was called after exitshell. This was changed so that it was called before exitshell because certain state needed to be reset in order for the EXIT trap to work. However, this caused issues because certain other states (such as local variables) should not be reset. This patch fixes this by creating a new function exitreset that is called prior to exitshell and moving reset back to its original location. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index dfe6d1c48..fbe8dd9e4 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -14284,11 +14284,11 @@ read_profile(const char *name) /* * This routine is called when an error or an interrupt occurs in an - * interactive shell and control is returned to the main command loop. - * (In dash, this function is auto-generated by build machinery). + * interactive shell and control is returned to the main command loop + * but prior to exitshell. */ static void -reset(void) +exitreset(void) { /* from eval.c: */ evalskip = 0; @@ -14301,14 +14301,23 @@ reset(void) /* from expand.c: */ ifsfree(); + /* from redir.c: */ + unwindredir(NULL); +} + +/* + * This routine is called when an error or an interrupt occurs in an + * interactive shell and control is returned to the main command loop. + * (In dash, this function is auto-generated by build machinery). + */ +static void +reset(void) +{ /* from input.c: */ g_parsefile->left_in_buffer = 0; g_parsefile->left_in_line = 0; /* clear input buffer */ popallfiles(); - /* from redir.c: */ - unwindredir(NULL); - /* from var.c: */ unwindlocalvars(NULL); } @@ -14356,13 +14365,16 @@ int ash_main(int argc UNUSED_PARAM, char **argv) smallint e; smallint s; - reset(); + exitreset(); e = exception_type; s = state; if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) { exitshell(); } + + reset(); + if (e == EXINT) { newline_and_flush(stderr); } -- cgit v1.2.3-55-g6feb From 74aaf05170d6f224194c98ee0434e2decae45735 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Feb 2020 12:11:26 +0100 Subject: ash: parser: Save/restore here-documents in command substitution Upstream comment: Date: Sat, 19 May 2018 02:39:42 +0800 parser: Save/restore here-documents in command substitution This patch changes the parsing of here-documents within command substitution, both old style and new style. In particular, the original here-document list is saved upon the beginning of parsing command substitution and restored when exiting. This means that here-documents outside of command substitution can no longer be filled by text within it and vice-versa. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index fbe8dd9e4..e0ddf7198 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12733,6 +12733,7 @@ parsebackq: { union node *n; char *str; size_t savelen; + struct heredoc *saveheredoclist; smallint saveprompt = 0; str = NULL; @@ -12808,6 +12809,9 @@ parsebackq: { *nlpp = stzalloc(sizeof(**nlpp)); /* (*nlpp)->next = NULL; - stzalloc did it */ + saveheredoclist = heredoclist; + heredoclist = NULL; + if (oldstyle) { saveprompt = doprompt; doprompt = 0; @@ -12817,18 +12821,21 @@ parsebackq: { if (oldstyle) doprompt = saveprompt; - else if (readtoken() != TRP) - raise_error_unexpected_syntax(TRP); + else { + if (readtoken() != TRP) + raise_error_unexpected_syntax(TRP); + setinputstring(nullstr); + parseheredoc(); + } + + heredoclist = saveheredoclist; (*nlpp)->n = n; - if (oldstyle) { - /* - * Start reading from old file again, ignoring any pushed back - * tokens left from the backquote parsing - */ - popfile(); + /* Start reading from old file again. */ + popfile(); + /* Ignore any pushed back tokens left from the backquote parsing. */ + if (oldstyle) tokpushback = 0; - } while (stackblocksize() <= savelen) growstackblock(); STARTSTACKSTR(out); -- cgit v1.2.3-55-g6feb From c55847fedbc3cbc10f2558c5449d8635f318ce49 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Feb 2020 15:59:08 +0100 Subject: ash: memalloc: Add growstackto helper Upstream commit: Date: Sat, 19 May 2018 02:39:46 +0800 memalloc: Add growstackto helper This patch adds the growstackto helper which repeatedly calls growstackblock until the requested size is reached. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index e0ddf7198..6505f4984 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -1740,6 +1740,15 @@ growstackstr(void) return (char *)stackblock() + len; } +static char * +growstackto(size_t len) +{ + while (stackblocksize() < len) + growstackblock(); + + return stackblock(); +} + /* * Called from CHECKSTRSPACE. */ @@ -1747,18 +1756,8 @@ static char * makestrspace(size_t newlen, char *p) { size_t len = p - g_stacknxt; - size_t size; - - for (;;) { - size_t nleft; - size = stackblocksize(); - nleft = size - len; - if (nleft >= newlen) - break; - growstackblock(); - } - return (char *)stackblock() + len; + return growstackto(len + newlen) + len; } static char * @@ -2584,9 +2583,7 @@ path_advance(const char **path, const char *name) for (p = start; *p && *p != ':' && *p != '%'; p++) continue; len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ - while (stackblocksize() < len) - growstackblock(); - q = stackblock(); + q = growstackto(len); if (p != start) { q = mempcpy(q, start, p - start); *q++ = '/'; @@ -12836,9 +12833,7 @@ parsebackq: { /* Ignore any pushed back tokens left from the backquote parsing. */ if (oldstyle) tokpushback = 0; - while (stackblocksize() <= savelen) - growstackblock(); - STARTSTACKSTR(out); + out = growstackto(savelen + 1); if (str) { memcpy(out, str, savelen); STADJUST(savelen, out); -- cgit v1.2.3-55-g6feb From b0d2dc7d62f6dea67b82e451510fa77243b4c60c Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Feb 2020 15:27:41 +0100 Subject: ash: exec: Do not allocate stack string in padvance Upstream commit: Date: Sat, 19 May 2018 02:39:48 +0800 exec: Do not allocate stack string in padvance Many callers of padvance immediately free the allocated string so this patch moves the stalloc call to the caller. Instead of returning the allocated string, padvance now returns the length to allocate (this may be longer than the actual string length, even including the NUL). For the case where we would previously return NULL, we now return -1. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 74 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 6505f4984..e89cecf0b 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2560,17 +2560,17 @@ listvars(int on, int off, struct strlist *lp, char ***end) /* ============ Path search helper * * The variable path (passed by reference) should be set to the start - * of the path before the first call; path_advance will update - * this value as it proceeds. Successive calls to path_advance will return + * of the path before the first call; padvance will update + * this value as it proceeds. Successive calls to padvance will return * the possible path expansions in sequence. If an option (indicated by * a percent sign) appears in the path entry then the global variable * pathopt will be set to point to it; otherwise pathopt will be set to * NULL. */ -static const char *pathopt; /* set by path_advance */ +static const char *pathopt; /* set by padvance */ -static char * -path_advance(const char **path, const char *name) +static int +padvance(const char **path, const char *name) { const char *p; char *q; @@ -2578,7 +2578,7 @@ path_advance(const char **path, const char *name) size_t len; if (*path == NULL) - return NULL; + return -1; start = *path; for (p = start; *p && *p != ':' && *p != '%'; p++) continue; @@ -2599,7 +2599,7 @@ path_advance(const char **path, const char *name) *path = p + 1; else *path = NULL; - return stalloc(len); + return len; } @@ -2840,6 +2840,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) char c; struct stat statb; int flags; + int len; flags = cdopt(); dest = *argptr; @@ -2869,9 +2870,10 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) if (!*dest) dest = "."; path = bltinlookup("CDPATH"); - while (path) { - c = *path; - p = path_advance(&path, dest); + while (p = path, (len = padvance(&path, dest)) >= 0) { + c = *p; + p = stalloc(len); + if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { if (c && c != ':') flags |= CD_PRINT; @@ -8169,13 +8171,13 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) } else { try_PATH: e = ENOENT; - while ((cmdname = path_advance(&path, prog)) != NULL) { + while (padvance(&path, argv[0]) >= 0) { + cmdname = stackblock(); if (--idx < 0 && pathopt == NULL) { tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); if (errno != ENOENT && errno != ENOTDIR) e = errno; } - stunalloc(cmdname); } } @@ -8208,9 +8210,9 @@ printentry(struct tblentry *cmdp) idx = cmdp->param.index; path = pathval(); do { - name = path_advance(&path, cmdp->cmdname); - stunalloc(name); + padvance(&path, cmdp->cmdname); } while (--idx >= 0); + name = stackblock(); out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); } @@ -8603,9 +8605,9 @@ describe_command(char *command, const char *path, int describe_command_verbose) p = command; } else { do { - p = path_advance(&path, command); - stunalloc(p); + padvance(&path, command); } while (--j >= 0); + p = stackblock(); } if (describe_command_verbose) { out1fmt(" is %s", p); @@ -11020,8 +11022,12 @@ chkmail(void) mpath = mpathset() ? mpathval() : mailval(); new_hash = 0; for (;;) { - p = path_advance(&mpath, nullstr); - if (p == NULL) + int len; + + len = padvance(&mpath, nullstr); + if (!len) + break; + p = stackblock(); break; if (*p == '\0') continue; @@ -13341,33 +13347,30 @@ cmdloop(int top) * search for the file, which is necessary to find sub-commands. */ static char * -find_dot_file(char *name) +find_dot_file(char *basename) { char *fullname; const char *path = pathval(); struct stat statb; + int len; /* don't try this for absolute or relative paths */ - if (strchr(name, '/')) - return name; + if (strchr(basename, '/')) + return basename; - while ((fullname = path_advance(&path, name)) != NULL) { + while ((len = padvance(&path, basename)) >= 0) { + fullname = stackblock(); if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { - /* - * Don't bother freeing here, since it will - * be freed by the caller. - */ - return fullname; + /* This will be freed by the caller. */ + return stalloc(len); } - if (fullname != name) - stunalloc(fullname); } /* not found in PATH */ #if ENABLE_ASH_BASH_SOURCE_CURDIR - return name; + return basename; #else - ash_msg_and_raise_error("%s: not found", name); + ash_msg_and_raise_error("%s: not found", basename); /* NOTREACHED */ #endif } @@ -13470,6 +13473,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) int e; int updatetbl; struct builtincmd *bcmd; + int len; /* If name contains a slash, don't use PATH or hash table */ if (strchr(name, '/') != NULL) { @@ -13561,10 +13565,8 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) e = ENOENT; idx = -1; loop: - while ((fullname = path_advance(&path, name)) != NULL) { - stunalloc(fullname); - /* NB: code below will still use fullname - * despite it being "unallocated" */ + while ((len = padvance(&path, name)) >= 0) { + fullname = stackblock(); idx++; if (pathopt) { if (prefix(pathopt, "builtin")) { @@ -13598,7 +13600,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) if (!S_ISREG(statb.st_mode)) continue; if (pathopt) { /* this is a %func directory */ - stalloc(strlen(fullname) + 1); + stalloc(len); /* NB: stalloc will return space pointed by fullname * (because we don't have any intervening allocations * between stunalloc above and this stalloc) */ -- cgit v1.2.3-55-g6feb From 6c4f87e411aa5375eaea5a5909a5c610e38c7e70 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Feb 2020 16:02:40 +0100 Subject: ash: exec: Stricter pathopt parsing Upstream comment: Date: Sat, 19 May 2018 02:39:50 +0800 exec: Stricter pathopt parsing This patch changes the parsing of pathopt. First of all only %builtin and %func (with arbitrary suffixes) will be recognised. Any other pathopt will be treated as a normal directory. Furthermore, pathopt can now be specified before the directory, rather than after it. In fact, a future version may remove support for pathopt suffixes. Wherever the pathopt is placed, an optional % may be placed after it to terminate the pathopt. This is so that it is less likely that a genuine directory containing a % sign is parsed as a pathopt. Users of padvance outside of exec.c have also been modified: 1) cd(1) will always treat % characters as part of the path. 2) chkmail will continue to accept arbitrary pathopt. 3) find_dot_file will ignore the %builtin pathopt instead of trying to do a stat in the accompanying directory (which is usually the current directory). The patch also removes the clearcmdentry optimisation where we attempt to only partially flush the table where possible. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 168 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 104 insertions(+), 64 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index e89cecf0b..c383cccda 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2557,8 +2557,31 @@ listvars(int on, int off, struct strlist *lp, char ***end) } -/* ============ Path search helper - * +/* ============ Path search helper */ +static const char * +legal_pathopt(const char *opt, const char *term, int magic) +{ + switch (magic) { + case 0: + opt = NULL; + break; + + case 1: + opt = prefix(opt, "builtin") ?: prefix(opt, "func"); + break; + + default: + opt += strcspn(opt, term); + break; + } + + if (opt && *opt == '%') + opt++; + + return opt; +} + +/* * The variable path (passed by reference) should be set to the start * of the path before the first call; padvance will update * this value as it proceeds. Successive calls to padvance will return @@ -2566,40 +2589,70 @@ listvars(int on, int off, struct strlist *lp, char ***end) * a percent sign) appears in the path entry then the global variable * pathopt will be set to point to it; otherwise pathopt will be set to * NULL. + * + * If magic is 0 then pathopt recognition will be disabled. If magic is + * 1 we shall recognise %builtin/%func. Otherwise we shall accept any + * pathopt. */ static const char *pathopt; /* set by padvance */ static int -padvance(const char **path, const char *name) +padvance_magic(const char **path, const char *name, int magic) { + const char *term = "%:"; + const char *lpathopt; const char *p; char *q; const char *start; + size_t qlen; size_t len; if (*path == NULL) return -1; + + lpathopt = NULL; start = *path; - for (p = start; *p && *p != ':' && *p != '%'; p++) - continue; - len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ - q = growstackto(len); - if (p != start) { - q = mempcpy(q, start, p - start); - *q++ = '/'; + + if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) { + lpathopt = start + 1; + start = p; + term = ":"; } - strcpy(q, name); - pathopt = NULL; + + len = strcspn(start, term); + p = start + len; + if (*p == '%') { - pathopt = ++p; - while (*p && *p != ':') - p++; + size_t extra = strchrnul(p, ':') - p; + + if (legal_pathopt(p + 1, term, magic)) + lpathopt = p + 1; + else + len += extra; + + p += extra; } - if (*p == ':') - *path = p + 1; - else - *path = NULL; - return len; + + pathopt = lpathopt; + *path = *p == ':' ? p + 1 : NULL; + + /* "2" is for '/' and '\0' */ + qlen = len + strlen(name) + 2; + q = growstackto(qlen); + + if (len) { + q = mempcpy(q, start, len); + *q++ = '/'; + } + strcpy(q, name); + + return qlen; +} + +static int +padvance(const char **path, const char *name) +{ + return padvance_magic(path, name, 1); } @@ -8217,11 +8270,10 @@ printentry(struct tblentry *cmdp) } /* - * Clear out command entries. The argument specifies the first entry in - * PATH which has changed. + * Clear out command entries. */ static void -clearcmdentry(int firstchange) +clearcmdentry(void) { struct tblentry **tblp; struct tblentry **pp; @@ -8231,10 +8283,8 @@ clearcmdentry(int firstchange) for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) { pp = tblp; while ((cmdp = *pp) != NULL) { - if ((cmdp->cmdtype == CMDNORMAL && - cmdp->param.index >= firstchange) - || (cmdp->cmdtype == CMDBUILTIN && - builtinloc >= firstchange) + if (cmdp->cmdtype == CMDNORMAL + || (cmdp->cmdtype == CMDBUILTIN && builtinloc > 0) ) { *pp = cmdp->next; free(cmdp); @@ -8334,7 +8384,7 @@ hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) char *name; if (nextopt("r") != '\0') { - clearcmdentry(0); + clearcmdentry(); return 0; } @@ -8395,42 +8445,28 @@ hashcd(void) * Called with interrupts off. */ static void FAST_FUNC -changepath(const char *new) +changepath(const char *newval) { - const char *old; - int firstchange; + const char *new; int idx; - int idx_bltin; + int bltin; - old = pathval(); - firstchange = 9999; /* assume no change */ + new = newval; idx = 0; - idx_bltin = -1; + bltin = -1; for (;;) { - if (*old != *new) { - firstchange = idx; - if ((*old == '\0' && *new == ':') - || (*old == ':' && *new == '\0') - ) { - firstchange++; - } - old = new; /* ignore subsequent differences */ + if (*new == '%' && prefix(new + 1, "builtin")) { + bltin = idx; + break; } - if (*new == '\0') + new = strchr(new, ':'); + if (!new) break; - if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) - idx_bltin = idx; - if (*new == ':') - idx++; + idx++; new++; - old++; } - if (builtinloc < 0 && idx_bltin >= 0) - builtinloc = idx_bltin; /* zap builtins */ - if (builtinloc >= 0 && idx_bltin < 0) - firstchange = 0; - clearcmdentry(firstchange); - builtinloc = idx_bltin; + builtinloc = bltin; + clearcmdentry(); } enum { TEOF, @@ -11024,7 +11060,7 @@ chkmail(void) for (;;) { int len; - len = padvance(&mpath, nullstr); + len = padvance_magic(&mpath, nullstr, 2); if (!len) break; p = stackblock(); @@ -13360,7 +13396,9 @@ find_dot_file(char *basename) while ((len = padvance(&path, basename)) >= 0) { fullname = stackblock(); - if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { + if ((!pathopt || *pathopt == 'f') + && !stat(fullname, &statb) && S_ISREG(statb.st_mode) + ) { /* This will be freed by the caller. */ return stalloc(len); } @@ -13566,17 +13604,19 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) idx = -1; loop: while ((len = padvance(&path, name)) >= 0) { + const char *lpathopt = pathopt; + fullname = stackblock(); idx++; - if (pathopt) { - if (prefix(pathopt, "builtin")) { + if (lpathopt) { + if (*lpathopt == 'b') { if (bcmd) goto builtin_success; continue; - } - if ((act & DO_NOFUNC) - || !prefix(pathopt, "func") - ) { /* ignore unimplemented options */ + } else if (!(act & DO_NOFUNC)) { + /* handled below */ + } else { + /* ignore unimplemented options */ continue; } } @@ -13599,7 +13639,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) e = EACCES; /* if we fail, this will be the error */ if (!S_ISREG(statb.st_mode)) continue; - if (pathopt) { /* this is a %func directory */ + if (lpathopt) { /* this is a %func directory */ stalloc(len); /* NB: stalloc will return space pointed by fullname * (because we don't have any intervening allocations -- cgit v1.2.3-55-g6feb From 22c75924daa41b7ea097796afd4baafa2fc99d05 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Feb 2020 16:20:05 +0100 Subject: ash: exec: Never rehash regular built-ins Upstream commit: Date: Sat, 19 May 2018 02:39:51 +0800 exec: Never rehash regular built-ins As regular (including special) built-ins can never be overridden, we should never remove them from the hash table. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index c383cccda..389db3cd0 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -8284,7 +8284,10 @@ clearcmdentry(void) pp = tblp; while ((cmdp = *pp) != NULL) { if (cmdp->cmdtype == CMDNORMAL - || (cmdp->cmdtype == CMDBUILTIN && builtinloc > 0) + || (cmdp->cmdtype == CMDBUILTIN + && !IS_BUILTIN_REGULAR(cmdp->param.cmd) + && builtinloc > 0 + ) ) { *pp = cmdp->next; free(cmdp); @@ -8403,7 +8406,11 @@ hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) cmdp = cmdlookup(name, 0); if (cmdp != NULL && (cmdp->cmdtype == CMDNORMAL - || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) + || (cmdp->cmdtype == CMDBUILTIN + && !IS_BUILTIN_REGULAR(cmdp->param.cmd) + && builtinloc > 0 + ) + ) ) { delete_cmd_entry(); } @@ -13556,7 +13563,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) bit = DO_NOFUNC; break; case CMDBUILTIN: - bit = DO_ALTBLTIN; + bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_ALTBLTIN; break; } if (act & bit) { -- cgit v1.2.3-55-g6feb