From 4928fc1187826887895b20201ea7db1341fb00f6 Mon Sep 17 00:00:00 2001 From: Michael Greenberg Date: Thu, 14 Dec 2023 09:08:42 -0500 Subject: [PATCH] Merge v0.5.12 from upstream (#27) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * options: Do not set commandname in procargs We set commandname in procargs when we don't have to. This results in a duplicated output of arg0 when an error occurs. Reported-by: Olivier Duclos Signed-off-by: Herbert Xu * expand: Fix double-decrement in argstr Due to a double decrement in argstr we may miss field separators at the end of a word in certain situations. Reported-by: Martijn Dekker Fixes: 3cd538634f71 ("expand: Do not reprocess data when...") Signed-off-by: Herbert Xu * eval: Reset handler when entering a subshell As it is a subshell can execute code that is only meant for the parent shell when it executes a longjmp that is caught by something like evalcommand. This patch fixes it by resetting the handler when entering a subshell. Reported-by: Martijn Dekker Signed-off-by: Herbert Xu * parser: Fix old-style command substitution here-document crash On Wed, Jul 25, 2018 at 12:38:27PM +0000, project-repo wrote: > Hi, > I am working on a project in which I use the honggfuzz fuzzer to fuzz open > source software and I decided to fuzz dash. In doing so I discovered a > NULL pointer dereference in src/redir.ch on line 305. Following is a > backtrace as supplied by the address sanitizer: > > AddressSanitizer:DEADLYSIGNAL > ================================================================= > ==39623==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000010 (pc 0x0000005768ed bp 0x7ffc00273df0 sp 0x7ffc00273c60 T0) > ==39623==The signal is caused by a READ memory access. > ==39623==Hint: address points to the zero page. > #0 0x5768ec in openhere /home/jfe/dash/src/redir.c:305:29 > #1 0x574d92 in openredirect /home/jfe/dash/src/redir.c:230:7 > #2 0x5737fe in redirect /home/jfe/dash/src/redir.c:121:11 > #3 0x576017 in redirectsafe /home/jfe/dash/src/redir.c:424:3 > #4 0x522326 in evalcommand /home/jfe/dash/src/eval.c:828:11 > #5 0x520010 in evaltree /home/jfe/dash/src/eval.c:288:12 > #6 0x5270da in evaltreenr /home/jfe/dash/src/eval.c:332:2 > #7 0x526f04 in evalbackcmd /home/jfe/dash/src/eval.c:640:3 > #8 0x539020 in expbackq /home/jfe/dash/src/expand.c:522:2 > #9 0x5332d7 in argstr /home/jfe/dash/src/expand.c:343:4 > #10 0x5322f7 in expandarg /home/jfe/dash/src/expand.c:196:2 > #11 0x528118 in fill_arglist /home/jfe/dash/src/eval.c:659:3 > #12 0x5213b6 in evalcommand /home/jfe/dash/src/eval.c:769:13 > #13 0x520010 in evaltree /home/jfe/dash/src/eval.c:288:12 > #14 0x554423 in cmdloop /home/jfe/dash/src/main.c:234:8 > #15 0x553bcc in main /home/jfe/dash/src/main.c:176:3 > #16 0x7f201c2b2a86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21a86) > #17 0x41dfb9 in _start (/home/jfe/dash/src/dash+0x41dfb9) > > AddressSanitizer can not provide additional info. > SUMMARY: AddressSanitizer: SEGV /home/jfe/dash/src/redir.c:305:29 in openhere > ==39623==ABORTING > > This bug can be reproduced by running "dash < min" where min is þhe file > attached. I was able to reproduce this bug with the current git version > and the current debian version. > > cheers > project-repo > > < `< Fixes: 51e2d88d6e51 ("parser: Save/restore here-documents in...") Signed-off-by: Herbert Xu * 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 * parser: Only accept single-digit parameter expansion outside of braces On Thu, Apr 25, 2019 at 01:39:52AM +0000, Michael Orlitzky wrote: > The POSIX spec says, > > The parameter name or symbol can be enclosed in braces, which are > optional except for positional parameters with more than one digit or > when parameter is a name and is followed by a character that could be > interpreted as part of the name. > > However, dash seems to diverge from that behavior when we get to $10: > > $ cat test.sh > echo $10 > > $ dash ./test.sh one two three four five six seven eight nine ten > ten > > $ bash ./test.sh one two three four five six seven eight nine ten > one0 This patch should fix the problem. Signed-off-by: Herbert Xu * shell: delete AC_PROG_YACC Signed-off-by: Herbert Xu * redir: Clear saved redirections in subshell When we enter a subshell we need to drop the saved redirections as otherwise a subsequent unwindredir could produce incorrect results. This patch does this by simply clearing redirlist. While we could actually free the memory underneath for subshells it isn't really worth the trouble for now. In order to ensure that this is done in every place where we enter a subshell, this patch adds a new mkinit hook called forkreset. The calls closescript, clear_traps and reset_handler are also added to the forkreset hook. This fixes a bug where the first two functions weren't called if we enter a subshell without forking. Reported-by: Harald van Dijk Signed-off-by: Herbert Xu * builtin: Fix seconds part of times(1) The seconds part of the times(1) built-in is wrong as it does not exclude the minutes part of the result. This patch fixes it. This problem was first noted by Michael Greenberg who also sent a similar patch. Reported-by: Michael Greenberg Signed-off-by: Herbert Xu * jobs: Rename DOWAIT_NORMAL to DOWAIT_NONBLOCK To make it clearer what it is doing: nonblocking wait() Signed-off-by: Denys Vlasenko Signed-off-by: Herbert Xu * var: Remove poplocalvars() always-zero argument, make it static Signed-off-by: Denys Vlasenko Signed-off-by: Herbert Xu * jobs: Fix infinite loop in waitproc After we changed the resetting of gotsigchld so that it is only done if jp is NULL, we can now get an infinite loop in waitproc if gotsigchld is set but there is no outstanding child because everything had been waited for previously without gotsigchld being zeroed. This patch fixes it by always zeroing gotsigchld as we did before. The bug that the previous patch was trying to fix is now resolved by switching the blocking mode to DOWAIT_NORMAL after the specified job has been completed so that we really do wait for all outstanding dead children. Reported-by: Harald van Dijk Fixes: 6c691b3e5099 ("jobs: Only clear gotsigchld when waiting...") Signed-off-by: Herbert Xu * parser: Fix handling of empty aliases Dash was incorrectly handling empty aliases. When attempting to use an empty alias with nothing else, I'm (incorrectly) prompted for more input: ``` $ alias empty='' $ empty > ``` Other shells (e.g., bash, yash) correctly handle the lone, empty alias as an empty command: ``` $ alias empty='' $ empty $ ``` The problem here is that we incorrectly enter the loop eating TNLs in readtoken(). This patch fixes it by setting checkkwd correctly. Reported-by: Michael Greenberg Signed-off-by: Herbert Xu * parser: Catch errors in expandstr On Fri, Dec 13, 2019 at 02:51:34PM +0000, Simon Ser wrote: > Just noticed another dash bug: when setting invalid PS1 values dash > enters an infinite loop. > > For instance, setting PS1='$(' makes dash print many of these: > > dash: 1: Syntax error: end of file unexpected (expecting ")") > > It would be nice to fallback to the default PS1 value on error. This patch fixes it by using the literal value of PS1 should an error occur during expansion. On Wed, Feb 26, 2020 at 09:12:04PM +0000, Ron Yorston wrote: > > There's another case that should be handled. PS1='`xxx(`' causes the > shell to exit because the old-style backquote leaves an additional file > on the stack. Ron's change has been folded into this patch. Reported-by: Simon Ser Reported-by: Ron Yorston Signed-off-by: Herbert Xu * parser: Fix alias expansion after heredoc or newlines This script should print OK: alias a="case x in " b=x a b) echo BAD;; esac alias BEGIN={ END=} BEGIN cat <<- EOF > /dev/null $(:) EOF END : <<- EOF && $(:) EOF BEGIN echo OK END However, because the value of checkkwd is either zeroed when it shouldn't, or isn't zeroed when it should, dash currently gets it wrong in every case. This patch fixes it by saving checkkwd and zeroing it where needed. Suggested-by: Harald van Dijk Reported-by: Harald van Dijk Reported-by: Martijn Dekker Signed-off-by: Herbert Xu * expand: Remove unused expandmeta() flag parameter Signed-off-by: Denys Vlasenko Signed-off-by: Herbert Xu * shell: mktokens relative TMPDIR The mktokens script fails when /tmp isn't writable (e.g., when building in a sandbox with a different TMPDIR). Replace absolute references to /tmp to relative references to TMPDIR. If TMPDIR is unset or null, default to /tmp. The mkbuiltins script was already hardened to work relative to TMPDIR, also defaulting to /tmp. v2 ensures that TMPDIR is quoted. v3 adds an extra quotation that prevents extra pathname expansions. Signed-off-by: Michael Greenberg Signed-off-by: Herbert Xu * input: Fix compiling against libedit with -fno-common With -fno-common, which will be enabled by default in GCC 10, we see this error: ld: input.o:(.bss+0x0): multiple definition of `el'; histedit.o:(.bss+0x8): first defined here To fix this, simply remove the definition as it is not needed. Signed-off-by: Jeroen Roovers Signed-off-by: Mike Gilbert Signed-off-by: Herbert Xu * shell: Always use explicit large file API There are some remaining stat/readdir calls in dash that may lead to spurious EOVERFLOW errors on 32-bit platforms. This patch changes them (as well as open(2)) to use the explicit large file API. Reported-by: Tatsuki Sugiura Signed-off-by: Herbert Xu * parser: Save and restore heredoclist in expandstr On Sun, May 17, 2020 at 01:19:28PM +0100, Harald van Dijk wrote: > > This still does not restore the state completely. It does not clean up any > pending heredocs. I see: > > $ PS1='$(< src/dash: 1: Syntax error: Unterminated quoted string > $(< > > > That is, after entering the ':' command, the shell is still trying to read > the heredoc from the prompt. This patch saves and restores the heredoclist in expandstr. It also removes a bunch of unnecessary volatiles as those variables are only referenced in case of a longjmp other than one started by a signal like SIGINT. Reported-by: Harald van Dijk Signed-off-by: Herbert Xu * shell: Fix typos Signed-off-by: Martin Michlmayr Signed-off-by: Herbert Xu * parser: Fix double-backslash nl in old-style command sub When handling backslashes within an old-style command substitution, we should not call pgetc_eatbnl because that would treat the next backslash character as another escape character if it was then followed by a new-line. This patch fixes it by calling pgetc. Reported-by: Matt Whitlock Fixes: 6bbc71d84bea ("parser: use pgetc_eatbnl() in more places") Signed-off-by: Herbert Xu * Release 0.5.11. * parser: Get rid of PEOA PEOA is a special character used to mark an alias as being finished so that we don't enter an infinite loop with nested aliases. It complicates the parser because we have to ensure that it is skipped where necessary and not copied to the resulting token text. This patch removes it and instead delays the marking of aliases until the second pgetc. This has the same effect as the current PEOA code while keeping the complexities within the input code. Signed-off-by: Herbert Xu * eval: Prevent recursive PS4 expansion Yaroslav Halchenko wrote: > > I like to (ab)use PS4 and set -x for tracing execution of scripts. > Reporting time and PID is very useful in this context. > > I am not 100% certain if bash's behavior (of actually running the command > embedded within PS4 string, probably eval'ing it) is actually POSIX > compliant, posh seems to not do that; but I think it is definitely not > desired for dash to just stall: > > - the script: > > #!/bin/sh > set -x > export PS4='+ $(date +%T.%N) [$$] ' > > echo "lets go" > sleep 1 > echo "done $var" > > - bash: > > /tmp > bash --posix test.sh > +export 'PS4=+ $(date +%T.%N) [$$] ' > +PS4='+ $(date +%T.%N) [$$] ' > + 09:15:48.982296333 [2764323] echo 'lets go' > lets go > + 09:15:48.987829613 [2764323] sleep 1 > + 09:15:49.994485037 [2764323] echo 'done ' > done > > > - posh: > exit:130 /tmp > posh test.sh > +export PS4=+ $(date +%T.%N) [$$] > + $(date +%T.%N) [$$] echo lets go > lets go > + $(date +%T.%N) [$$] sleep 1 > + $(date +%T.%N) [$$] echo done > done > > - dash: (stalls it set -x) > > /tmp > dash test.sh > +export PS4=+ $(date +%T.%N) [$$] > ^C^C This patch fixes the infinite loop caused by repeated expansions of PS4. Reported-by: Yaroslav Halchenko Signed-off-by: Herbert Xu * redir: Retry open64 on EINTR It is possible for open64 to block on named pipes, and therefore it can be interrupted by signals and return EINTR. We should only let it fail with EINTR if real signals are pending (i.e., it should not fail on SIGCHLD if SIGCHLD has not been trapped). This patch adds a new helper sh_open to retry the open64 call if necessary. It also calls sh_error when appropriate. Fixes: 3800d4934391 ("[JOBS] Fix dowait signal race") Reported-by: Samuel Thibault Signed-off-by: Herbert Xu * shell: Enable fnmatch/glob by default As fnmatch(3) and glob(3) from glibc are now working consistently, this patch enables them by default. Signed-off-by: Herbert Xu * expand: Make glob(3) interruptible by SIGINT If glob(3) is used then it can't be interrupted by SIGINT. This is bad when an expansion causes a large number of entries to be generated. This patch improves things by adding an int_pending check to gl_opendir call. Note that this is still not perfect, e.g., the sort would still be uninterruptible. Signed-off-by: Herbert Xu * error: Remove USE_NORETURN ifdef The USE_NORETURN was added because gcc was buggy almost 20 years ago. This is no longer needed and this patch removes it. Signed-off-by: Herbert Xu * jobs: Fix waitcmd busy loop We need to clear gotsigchld in waitproc because it is used as a loop conditional for the waitcmd case. Without it waitcmd may busy loop after a SIGCHLD. This patch also changes gotsigchld into a volatile sig_atomic_t to prevent compilers from optimising its accesses away. Fixes: 6c691b3e5099 ("jobs: Only clear gotsigchld when waiting...") Signed-off-by: Herbert Xu * eval: Check nflag in evaltree instead of cmdloop This patch moves the nflag check from cmdloop into evaltree. This is so that nflag will be in force even if we enter the shell via a path other than cmdloop, e.g., through sh -c. Reported-by: Joey Hess Signed-off-by: Herbert Xu * man: fix formatting Fix formatting according to the output of "mandoc -Tlint". Overview: Start each sentence on a new line. Protect a punctuation mark in a macro call with '\&'. Trim trailing space. Add a missing comma in a row of words. Use an en-dash instead of '--' if there is space around it. An em-dash is used without space around it. Comment out ".Pp" macros that do nothing. Split long sentences after a punctuation mark. Remove a "-width ..." for a ".Bl -item" macro, as it has no influence Details: mandoc: ./src/bltin/echo.1:69:38: WARNING: new sentence, new line mandoc: ./src/bltin/echo.1:75:35: WARNING: new sentence, new line mandoc: ./src/bltin/printf.1:205:12: WARNING: skipping empty macro: No mandoc: ./src/bltin/printf.1:284:28: STYLE: whitespace at end of input line mandoc: ./src/bltin/printf.1:288:20: STYLE: whitespace at end of input line mandoc: ./src/bltin/printf.1:293:28: STYLE: whitespace at end of input line mandoc: ./src/bltin/printf.1:353:31: WARNING: new sentence, new line mandoc: ./src/bltin/printf.1:74:2: STYLE: useless macro: Tn mandoc: ./src/bltin/printf.1:111:2: STYLE: useless macro: Tn mandoc: ./src/bltin/printf.1:116:2: STYLE: useless macro: Tn mandoc: ./src/bltin/printf.1:279:2: STYLE: useless macro: Tn mandoc: ./src/bltin/printf.1:334:2: WARNING: unusual Xr punctuation: none before vis(3) mandoc: ./src/bltin/printf.1:334:2: WARNING: unusual Xr order: vis(3) after printf(9) mandoc: ./src/bltin/printf.1:348:2: STYLE: useless macro: Tn mandoc: ./src/bltin/printf.1:333:6: STYLE: referenced manual not found: Xr printf 9 mandoc: ./src/bltin/printf.1:334:6: STYLE: referenced manual not found: Xr vis 3 mandoc: ./src/bltin/test.1:46:16: WARNING: skipping empty macro: Cm mandoc: ./src/bltin/test.1:105:5: STYLE: useless macro: Tn mandoc: ./src/dash.1:1180:58: WARNING: new sentence, new line mandoc: ./src/dash.1:1186:13: STYLE: whitespace at end of input line mandoc: ./src/dash.1:1194:38: WARNING: new sentence, new line mandoc: ./src/dash.1:1200:35: WARNING: new sentence, new line mandoc: ./src/dash.1:1474:71: WARNING: new sentence, new line mandoc: ./src/dash.1:1783:62: WARNING: new sentence, new line mandoc: ./src/dash.1:2061:22: WARNING: new sentence, new line mandoc: ./src/dash.1:2311:54: WARNING: new sentence, new line mandoc: ./src/dash.1:2315:63: WARNING: new sentence, new line mandoc: ./src/dash.1:37:2: WARNING: prologue macros out of order: Dt after Os mandoc: ./src/dash.1:87:2: STYLE: useless macro: Tn mandoc: ./src/dash.1:94:2: STYLE: useless macro: Tn mandoc: ./src/dash.1:343:2: STYLE: useless macro: Tn mandoc: ./src/dash.1:442:17: STYLE: verbatim "--", maybe consider using \(em mandoc: ./src/dash.1:466:2: STYLE: useless macro: Tn mandoc: ./src/dash.1:581:34: STYLE: verbatim "--", maybe consider using \(em mandoc: ./src/dash.1:583:25: STYLE: verbatim "--", maybe consider using \(em mandoc: ./src/dash.1:585:43: STYLE: verbatim "--", maybe consider using \(em mandoc: ./src/dash.1:595:11: STYLE: verbatim "--", maybe consider using \(em mandoc: ./src/dash.1:618:29: STYLE: verbatim "--", maybe consider using \(em mandoc: ./src/dash.1:697:2: WARNING: skipping paragraph macro: Pp before Bd mandoc: ./src/dash.1:1344:2: STYLE: useless macro: Tn mandoc: ./src/dash.1:1420:2: WARNING: skipping paragraph macro: Pp before Bd mandoc: ./src/dash.1:1434:2: WARNING: skipping paragraph macro: Pp before Bd mandoc: ./src/dash.1:1556:2: STYLE: useless macro: Tn mandoc: ./src/dash.1:1587:2: STYLE: useless macro: Tn mandoc: ./src/dash.1:1746:2: STYLE: useless macro: Tn mandoc: ./src/dash.1:1875:5: STYLE: useless macro: Tn mandoc: ./src/dash.1:1525:2: WARNING: skipping paragraph macro: Pp before It mandoc: ./src/dash.1:2182:2: WARNING: skipping paragraph macro: Pp before It mandoc: ./src/dash.1:2247:2: WARNING: sections out of conventional order: Sh ENVIRONMENT mandoc: ./src/dash.1:2323:11: WARNING: skipping -width argument: Bl -item mandoc: ./src/dash.1:2347:31: STYLE: consider using OS macro: Nx mandoc: ./src/dash.1:92:6: STYLE: referenced manual not found: Xr ksh 1 (2 times) mandoc: ./src/dash.1:253:6: STYLE: referenced manual not found: Xr emacs 1 mandoc: ./src/dash.1:2253:9: STYLE: referenced manual not found: Xr passwd 4 mandoc: ./src/dash.1:2330:6: STYLE: referenced manual not found: Xr csh 1 Signed-off-by: Bjarni Ingi Gislason Signed-off-by: Herbert Xu * shell: Group readdir64/dirent64 with open64 The test for open64 is separate from stat64 for macOS. However, the newly introduced tests for readdir64/dirent64 should be grouped with open64 instead of stat64 as otherwise they cause similar build failures. Reported-by: Martijn Dekker Signed-off-by: Herbert Xu * shell: Disable glob again as it strips traing slashes On Mon, Nov 16, 2020 at 01:47:48PM +1100, Herbert Xu wrote: > René Scharfe wrote: > > > > on Debian testing dash eats trailing slashes of parameters that happen > > to be regular files when expanding "$@". Example: > > > > $ rm -f foo bar > > $ touch foo > > $ dash -c 'echo "$0" "$@"' baz foo/ bar/ ./ > > baz foo bar/ ./ > > In fact you just have to do > > dash -c 'echo bar\/' > > This is a bug in glob(3). It's stripping the slash. > > I guess we'll just have to disable glob again. This patch disables glob(3) by default. Reported-by: René Scharfe Signed-off-by: Herbert Xu * jobs: Only block in waitcmd on first run This patch ensures that waitcmd never blocks unless there are outstanding jobs. This could otherwise trigger a hang if children were created prior to the shell coming into existence, or if there are backgrounded children of other kinds (e.g., a here- document). Fixes: 6c691b3e5099 ("jobs: Only clear gotsigchld when waiting...") Reported-by: Michael Biebl Signed-off-by: Herbert Xu * shell: Fail if building --with-libedit and can't find libedit Previously, configure --with-libedit would only fail in the case where libedit is available but its header file histedit.h is not. Fixes: 13537aaa484b ("[BUILD] Added --with-libedit option to...") Signed-off-by: Herbert Xu * input: Clear unget on RESET On Sat, Dec 19, 2020 at 02:23:44PM +0100, Denys Vlasenko wrote: > Current git: > > $ ;l > dash: 1: Syntax error: ";" unexpected > $ s > COPYING ChangeLog.O Makefile.am aclocal.m4 autom4te.cache > config.h config.log configure dash > dollar_altvalue1.tests missing stamp-h1 > ChangeLog Makefile Makefile.in autogen.sh compile > config.h.in config.status configure.ac depcomp install-sh > src trace This patch fixes it by clearing ungetc on RESET. Fixes: 17db43b58415 ("input: Allow two consecutive calls to pungetc") Reported-by: Denys Vlasenko Signed-off-by: Herbert Xu * jobs: Block signals during tcsetpgrp Harald van Dijk wrote: > On 19/12/2020 22:21, Steffen Nurpmeso wrote: >> Steffen Nurpmeso wrote in >> <20201219172838.1B-WB%steffen@sdaoden.eu>: >> |Long story short, after falsely accusing BSD make of not working >> >> After dinner i shortened it a bit more, and attach it again, ok? >> It is terrible, but now less redundant than before. >> Sorry for being so terse, that problem crosses my head for about >> a week, and i was totally mislead and if you bang your head >> against the wall so many hours bugs or misbehaviours in a handful >> of other programs is not the expected outcome. > > I think a minimal test case is simply > > all: > $(SHELL) -c 'trap "echo TTOU" TTOU; set -m; echo all good' > > unless I accidentally oversimplified. > > The SIGTTOU is caused by setjobctl's xtcsetpgrp(fd, pgrp) call to make > its newly started process group the foreground process group when job > control is enabled, where xtcsetpgrp is a wrapper for tcsetpgrp. (That's > in dash, the other variants may have some small differences.) tcsetpgrp > has this little bit in its specification: > > Attempts to use tcsetpgrp() from a process which is a member of > a background process group on a fildes associated with its con‐ > trolling terminal shall cause the process group to be sent a > SIGTTOU signal. If the calling thread is blocking SIGTTOU sig‐ > nals or the process is ignoring SIGTTOU signals, the process > shall be allowed to perform the operation, and no signal is > sent. > > Ordinarily, when job control is enabled, SIGTTOU is ignored. However, > when a trap action is specified for SIGTTOU, the signal is not ignored, > and there is no blocking in place either, so the tcsetpgrp() call is not > allowed. > > The lowest impact change to make here, the one that otherwise preserves > the existing shell behaviour, is to block signals before calling > tcsetpgrp and unblocking them afterwards. This ensures SIGTTOU does not > get raised here, but also ensures that if SIGTTOU is sent to the shell > for another reason, there is no window where it gets silently ignored. > > Another way to fix this is by not trying to make the shell start a new > process group, or at least not make it the foreground process group. > Most other shells appear to not try to do this. This patch implements the blocking of SIGTTOU (and everything else) while we call tcsetpgrp. Reported-by: Steffen Nurpmeso Signed-off-by: Herbert Xu * jobs: Always reset SIGINT/SIGQUIT handlers On Fri, Jan 08, 2021 at 08:55:41PM +0000, Harald van Dijk wrote: > On 18/05/2018 19:39, Herbert Xu wrote: > > This patch adds basic vfork support for the case of a simple command. > > ... @@ -879,17 +892,30 @@ forkchild(struct job *jp, union node *n, int > > mode) > > } > > } > > if (!oldlvl && iflag) { > > - setsignal(SIGINT); > > - setsignal(SIGQUIT); > > + if (mode != FORK_BG) { > > + setsignal(SIGINT); > > + setsignal(SIGQUIT); > > + } > > setsignal(SIGTERM); > > } > > + > > + if (lvforked) > > + return; > > + > > for (jp = curjob; jp; jp = jp->prev_job) > > freejob(jp); > > } > > This leaves SIGQUIT ignored in background jobs in interactive shells. > > ENV= dash -ic 'dash -c "kill -QUIT \$\$; echo huh" & wait' > > As of dash 0.5.11, this prints "huh". Before, the subprocess process killed > itself before it could print anything. Other shells do not leave SIGQUIT > ignored. > > (In a few other shells, this also prints "huh", but in those other shells, > that is because the inner shell chooses to ignore SIGQUIT, not because the > outer shell leaves it ignored.) Thanks for catching this. I have no idea how that got in there and it makes no sense whatsoever. This patch removes the if conditional. Fixes: e94a964e7dd0 ("eval: Add vfork support") Reported-by: Harald van Dijk Signed-off-by: Herbert Xu * eval: Do not cache value of eflag in evaltree Patrick Brünn wrote: > > Since we are migrating to Debian bullseye, we discovered a new behavior > with our scripts, which look like this: >>#!/bin/sh >>cleanup() { >> set +e^M >> rmdir "" >>} >>set -eu >>trap 'cleanup' EXIT INT TERM >>echo 'Hello world!' > > With old dash v0.5.10.2 this script would return 0 as we expected it. > But since commit 62cf6955f8abe875752d7163f6f3adbc7e49ebae it returns > the last exit code of our cleanup function. > Reverting that commit gives a merge conflict, but it seems to fix _our_ > problem. As that topic appears too complex to us I want to ask the > experts here: > > Is this change in behavior intended, by dash? > > Our workaround at the moment would be: >>trap 'cleanup || true' EXIT INT TERM Thanks for the report. This is actually a fairly old bug with set -e that's just been exposed by the exit status change. What's really happening is that cleanup itself is triggering a set -e exit incorrectly because evaltree cached the value of eflag prior to the function call. This patch should fix the problem. Reported-by: Patrick Brünn Signed-off-by: Herbert Xu Tested-by: Patrick Brünn Signed-off-by: Herbert Xu * shell: Call CHECK_DECL on stat64 On macOS it is possible to find stat64 at link-time but not at compile-time. To make the build process more robust we should check for the header file as well as the library. Reported-by: Saagar Jha Signed-off-by: Herbert Xu * parser: Fix VSLENGTH parsing with trailing garbage On Sat, Jun 19, 2021 at 02:44:46PM +0200, Denys Vlasenko wrote: > > CTLVAR and CTLBACKQ are not properly handled if encountered > inside {$#...}. Testcase: > > dash -c "`printf 'echo ${#1\x82}'`" 00 111 222 > > It should execute "echo ${#1 }" and thus print "3" > (the length of $1, which is "111"). > > Instead, it segfaults. > > (Ideally, it should fail since "1 " is not a valid > variable name, but currently dash accepts e.g. "${#1abc}" > as if it is "${#1}bc". A separate, less serious bug...). In fact these two bugs are one and the same. This patch fixes both by detecting the invalid substitution and not emitting it into the node tree. Incidentally this reveals a bug in how we parse ${#10} that got introduced recently, which is also fixed here. Reported-by: Denys Vlasenko Fixes: 7710a926b321 ("parser: Only accept single-digit parameter...") Signed-off-by: Herbert Xu * input: Remove special case for unget EOF Commit 17db43b5841504b694203952fb0e82246c06a97f (input: Allow two consecutive calls to pungetc) ensures that EOF is handled like any other character with respect to unget. As a result it's possible to remove the special case for unget of EOF in preadbuffer. Signed-off-by: Ron Yorston Signed-off-by: Herbert Xu * expand: Always quote caret when using fnmatch This patch forces ^ to be a literal when we use fnmatch. In order to allow for the extra space to quote the caret, the function _rmescapes will allocate up to twice the memory if the flag RMESCAPE_GLOB is set. Fixes: 7638476c18f2 ("shell: Enable fnmatch/glob by default") Reported-by: Christoph Anton Mitterer Suggested-by: Harald van Dijk Signed-off-by: Herbert Xu * expand: Add ifsfree to expand to fix a logic error that causes a buffer over-read On Mon, Jun 20, 2022 at 02:27:10PM -0400, Alex Gorinson wrote: > Due to a logic error in the ifsbreakup function in expand.c if a > heredoc and normal command is run one after the other by means of a > semi-colon, when the second command drops into ifsbreakup the command > will be evaluated with the ifslastp/ifsfirst struct that was set when > the here doc was evaluated. This results in a buffer over-read that > can leak the program's heap, stack, and arena addresses which can be > used to beat ASLR. > > Steps to Reproduce: > First bug: > cmd args: ~/exampleDir/example> dash > $ M='AAAAAAAAAAAAAAAAA' > $ q00(){ > $ <<000;echo > $ ${D?$M$M$M$M$M$M} > $ 000 > $ } > $ q00 should be echo'd out; this works with ash, busybox ash, and dash and > with all option args.> > > Patch: > Adding the following to expand.c will fix both bugs in one go. > (Thank you to Harald van Dijk and Michael Greenberg for doing the > heavy lifting for this patch!) > ========================== > --- a/src/expand.c > +++ b/src/expand.c > @@ -859,6 +859,7 @@ > if (discard) > return -1; > > +ifsfree(); > sh_error("Bad substitution"); > } > > @@ -1739,6 +1740,7 @@ > } else > msg = umsg; > } > +ifsfree(); > sh_error("%.*s: %s%s", end - var - 1, var, msg, tail); > } > ========================== Thanks for the report! I think it's better to add the ifsfree() call to the exception handling path as other sh_error calls may trigger this too. Reported-by: Alex Gorinson Signed-off-by: Herbert Xu * eval: Always set exitstatus in evaltree There is no harm in setting exitstatus unconditionally in evaltree. Signed-off-by: Herbert Xu * eval: Check eflag after redirection error > > This is a POSIX violation, and quite a grave one at that: > set -e is oft[1] used to guard against precisely this type of error! > > The same happens if set -e is executed. > > All quotes POSIX.1, Issue 7, TC2: > sh, OPTIONS: > > The -a, -b, -C, -e, -f, -m, -n, -o option, -u, -v, and -x options > > are described as part of the set utility in Special Built-In > > Utilities. > > set, DESCRIPTION, -e: > > When this option is on, when any command fails (for any of the > > reasons listed in Consequences of Shell Errors or by returning an > > exit status greater than zero), the shell immediately shall exit, as > > if by executing the exit special built-in utility with no arguments, > > with the following exceptions: > > > > 1. The failure of any individual command in a multi-command pipeline > > shall not cause the shell to exit. Only the failure of the > > pipeline itself shall be considered. > > 2. The -e setting shall be ignored when executing the compound list > > following the while, until, if, or elif reserved word, a pipeline > > beginning with the ! reserved word, or any command of an AND-OR > > list other than the last. > > 3. If the exit status of a compound command other than a subshell > > command was the result of a failure while -e was being ignored, > > then -e shall not apply to this command. > > XCU, 2.9.4: Shell Command Language, Shell Commands, Compound Commands: > The while Loop: > > The format of the while loop is as follows: > > > > while compound-list-1 > > do > > compound-list-2 > > done > (until is equivalent). > The if Conditional Construct: > > The format for the if construct is as follows: > > > > if compound-list > > then > > compound-list > > [elif compound-list > > then > > compound-list] ... > > [else > > compound-list] > > fi > > It follows, therefore, that > * Exception 1. does not apply as there is no pipeline > * Exception 2. does not apply, as the redirection does /not/ follow > "while" or "if" directly and is /not/ part of the conditional > compound-list > * in the "for" case, there is no such provision, so this is likely not > a confusion w.r.t. the conditional compound-lists > * Exception 3. does not apply as -e was not being ignored while the > compound commands were being executed (indeed, the compound commands > do not run at all, as evidenced by the program terminating) > > [1]: https://salsa.debian.org/glibc-team/glibc/-/merge_requests/6#note_329899 > ----- End forwarded message ----- Yes we should check the exit status after redirections. Reported-by: наб Signed-off-by: Herbert Xu * parser: Add VSBIT to ensure subtype is never zero Harald van Dijk wrote: > On 21/11/2022 13:08, Harald van Dijk wrote: >> On 21/11/2022 02:38, Christoph Anton Mitterer wrote: >>> reject_filtered_cmd() >>> { >>> reject_and_die "disallowed command${restrict_path_list:+ >>> (restrict-path: \"${restrict_path_list//|/\", \"}\")}" >>> } >>> >>> reject_filtered_cmd >>[...] >> This should either result in the ${...//...} being skipped, or the "Bad >> substitution" error. Currently, what happens instead is it attempts, but >> fails, to skip the ${...//...}. > > The reason it fails is because the word is cut off. > > Variable substitutions are encoded as a CTLVAR special character, > followed by a byte indicating the type of substitution, followed by the > rest of the substitution data. The type of substitution is the VSNORMAL, > VSMINUS, etc. seen in parser.h. An invalid substitution is encoded as a > value of 0. > > When we define a function, we clone the function body in order to > preserve it. Cloning the function body is done by cloning each node. > Cloning a "word" node (NARG) involves copying the characters that make > up the word up to and including the terminating null byte. > > These two interact badly. The invalid substitution is seen as > terminating the word, the rest of the word is not copied, but the > expansion code does not have any way of seeing that anything got cut off > and happily continues attempting to process the rest of the word. > > If dash decides to issue an error in this case, this is not a problem: > the null byte is guaranteed to be copied, and if processing is > guaranteed to stop if a null byte is encountered, everything works out. > > If dash decides to not issue an error in this case, the encoding of bad > substitutions needs to change to a non-null byte. It appears that if we > set the byte to VSNUL, the expansion logic is already able to handle it, > but I have not tested this extensively. Thanks for the analysis Harald! This patch does basically what you've described except it uses a new bit to avoid any confusion with a genuine VSNUL. Fixes: 3df3edd13389 ("[PARSER] Report substition errors at...") Reported-by: Christoph Anton Mitterer Signed-off-by: Herbert Xu Cheers, Signed-off-by: Herbert Xu * eval: Test evalskip before flipping status for NNOT On Tue, Dec 06, 2022 at 10:15:03AM +0000, Harald van Dijk wrote: > > There is a long-standing bug that may or may not be harder to fix if this > patch goes in, depending on how you want to fix it. Here's a script that > already fails on current dash. > > f() { > if ! return 0 > then : > fi > } > f > > This should return 0, and does return 0 in bash and ksh (and almost all > shells), but returns 1 in dash. > > There are a few possible ways of fixing it. Some of them rely on continuing > to conditionally set exitstatus. This can be fixed simply by testing evalskip prior to flipping the status. Reported-by: Harald van Dijk Signed-off-by: Herbert Xu * Release 0.5.12. --------- Signed-off-by: Herbert Xu Signed-off-by: Nikolai Merinov Signed-off-by: Denys Vlasenko Signed-off-by: Michael Greenberg Signed-off-by: Jeroen Roovers Signed-off-by: Mike Gilbert Signed-off-by: Martin Michlmayr Signed-off-by: Bjarni Ingi Gislason Signed-off-by: Ron Yorston Co-authored-by: Herbert Xu Co-authored-by: Nikolai Merinov Co-authored-by: Fangrui Song Co-authored-by: Denys Vlasenko Co-authored-by: Jeroen Roovers Co-authored-by: Martin Michlmayr Co-authored-by: Bjarni Ingi Gislason Co-authored-by: C. McEnroe Co-authored-by: Ron Yorston --- configure.ac | 22 ++++++--- src/bltin/echo.1 | 6 ++- src/bltin/printf.1 | 13 ++--- src/bltin/test.1 | 2 +- src/dash.1 | 57 ++++++++++++---------- src/error.h | 4 -- src/eval.c | 40 +++++++++++----- src/expand.c | 115 +++++++++++++++++++++++++++++++-------------- src/expand.h | 3 ++ src/input.c | 102 ++++++++++++++++++++++------------------ src/input.h | 7 ++- src/jobs.c | 19 ++++---- src/main.c | 2 +- src/mksyntax.c | 16 +++---- src/mktokens | 1 - src/mystring.c | 9 +++- src/mystring.h | 9 +++- src/parser.c | 34 ++++---------- src/parser.h | 1 + src/redir.c | 104 ++++++++++++++++++++++++---------------- src/redir.h | 1 + 21 files changed, 342 insertions(+), 225 deletions(-) diff --git a/configure.ac b/configure.ac index 024083a..2c7d31b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([dash],[0.5.11.5]) +AC_INIT([dash],[0.5.12]) AM_INIT_AUTOMAKE([foreign subdir-objects]) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADERS(config.h) @@ -14,6 +14,7 @@ AC_PROG_YACC dnl MMG 2018-09-26 support building the library AM_PROG_AR + AC_MSG_CHECKING([for build system compiler]) if test "$cross_compiling" = yes; then CC_FOR_BUILD=${CC_FOR_BUILD-cc} @@ -41,8 +42,8 @@ if test "$enable_static" = "yes"; then export LDFLAGS="-static -Wl,--fatal-warnings" fi -AC_ARG_ENABLE(fnmatch, AS_HELP_STRING(--enable-fnmatch, \ - [Use fnmatch(3) from libc])) +AC_ARG_ENABLE(fnmatch, AS_HELP_STRING(--disable-fnmatch, \ + [Do not use fnmatch(3) from libc])) AC_ARG_ENABLE(glob, AS_HELP_STRING(--enable-glob, [Use glob(3) from libc])) dnl Checks for libraries. @@ -126,7 +127,7 @@ if test "$enable_test_workaround" = "yes"; then [Define if your faccessat tells root all files are executable]) fi -if test "$enable_fnmatch" = yes; then +if test "$enable_fnmatch" != no; then use_fnmatch= AC_CHECK_FUNCS(fnmatch, use_fnmatch=yes) fi @@ -144,10 +145,18 @@ if test "$ac_cv_func_signal" != yes; then fi dnl Check for stat64 (dietlibc/klibc). -AC_CHECK_DECL(stat64,, [ +AC_CHECK_DECL(stat64, AC_CHECK_FUNC(stat64)) +if test "$ac_cv_func_stat64" != yes; then AC_DEFINE(fstat64, fstat, [64-bit operations are the same as 32-bit]) AC_DEFINE(lstat64, lstat, [64-bit operations are the same as 32-bit]) AC_DEFINE(stat64, stat, [64-bit operations are the same as 32-bit]) +fi + +AC_CHECK_FUNC(glob64,, [ + AC_DEFINE(glob64_t, glob_t, [64-bit operations are the same as 32-bit]) + AC_DEFINE(glob64, glob, [64-bit operations are the same as 32-bit]) + AC_DEFINE(globfree64, globfree, + [64-bit operations are the same as 32-bit]) ]) dnl OS X apparently has stat64 but not open64. @@ -179,7 +188,8 @@ if test "$with_libedit" = "yes"; then AC_CHECK_LIB(edit, history_init, [ AC_CHECK_HEADER([histedit.h], [use_libedit="yes"], AC_MSG_ERROR( - [Can't find required header files.]))]) + [Can't find required header files.]))], [ + AC_MSG_ERROR([Can't find libedit.])]) fi if test "$use_libedit" != "yes"; then AC_DEFINE([SMALL], 1, [Define if you build with -DSMALL]) diff --git a/src/bltin/echo.1 b/src/bltin/echo.1 index fbc7fb4..4d1890f 100644 --- a/src/bltin/echo.1 +++ b/src/bltin/echo.1 @@ -66,13 +66,15 @@ and may be given. .Pp If any of the following sequences of characters is encountered during -output, the sequence is not output. Instead, the specified action is +output, the sequence is not output. +Instead, the specified action is performed: .Bl -tag -width indent .It Li \eb A backspace character is output. .It Li \ec -Subsequent output is suppressed. This is normally used at the end of the +Subsequent output is suppressed. +This is normally used at the end of the last argument to suppress the trailing newline that .Nm would otherwise output. diff --git a/src/bltin/printf.1 b/src/bltin/printf.1 index 3873173..409d434 100644 --- a/src/bltin/printf.1 +++ b/src/bltin/printf.1 @@ -202,7 +202,7 @@ and formats, or the maximum number of characters to be printed from a string .Sm off -.Pf ( Cm b No , +.Pf ( Cm b Ns \&, .Sm on .Cm B and @@ -281,16 +281,16 @@ value is the 1\-, 2\-, or 3\-digit octal number .Ar num . .It Cm \e^ Ns Ar c -Write the control character +Write the control character .Ar c . Generates characters `\e000' through `\e037`, and `\e177' (from `\e^?'). .It Cm \eM\- Ns Ar c -Write the character +Write the character .Ar c with the 8th bit set. Generates characters `\e241' through `\e376`. .It Cm \eM^ Ns Ar c -Write the control character +Write the control character .Ar c with the 8th bit set. Generates characters `\e000' through `\e037`, and `\e177' (from `\eM^?'). @@ -330,7 +330,7 @@ exits 0 on success, 1 on failure. .Sh SEE ALSO .Xr echo 1 , .Xr printf 3 , -.Xr printf 9 +.Xr printf 9 , .Xr vis 3 .Sh STANDARDS The @@ -350,5 +350,6 @@ to floating-point and then back again, floating-point precision may be lost. .Pp Hexadecimal character constants are restricted to, and should be specified -as, two character constants. This is contrary to the ISO C standard but +as, two character constants. +This is contrary to the ISO C standard but does guarantee detection of the end of the constant. diff --git a/src/bltin/test.1 b/src/bltin/test.1 index 42435fb..03abce8 100644 --- a/src/bltin/test.1 +++ b/src/bltin/test.1 @@ -43,7 +43,7 @@ .Nm test .Ar expression .Nm \&[ -.Ar expression Cm ] +.Ar expression Cm \&] .Sh DESCRIPTION The .Nm test diff --git a/src/dash.1 b/src/dash.1 index 32f6ac0..ff02237 100644 --- a/src/dash.1 +++ b/src/dash.1 @@ -33,8 +33,8 @@ .\" @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" .Dd January 19, 2003 -.Os .Dt DASH 1 +.Os .Sh NAME .Nm dash .Nd command interpreter (shell) @@ -439,7 +439,7 @@ instead of then leading tabs in the here-doc-text are stripped. .Ss Search and Execution There are three types of commands: shell functions, builtin commands, and -normal programs -- and the command is searched for (by name) in that order. +normal programs \(en and the command is searched for (by name) in that order. They each are executed in a different way. .Pp When a shell function is executed, all of the shell positional parameters @@ -578,11 +578,11 @@ the preceding AND-OR-list. .Pp Note that unlike some other shells, each process in the pipeline is a child of the invoking shell (unless it is a shell builtin, in which case -it executes in the current shell -- but any effect it has on the +it executes in the current shell \(en but any effect it has on the environment is wiped). -.Ss Background Commands -- & +.Ss Background Commands \(en & If a command is terminated by the control operator ampersand (&), the -shell executes the command asynchronously -- that is, the shell does not +shell executes the command asynchronously \(en that is, the shell does not wait for the command to finish before executing the next command. .Pp The format for running a command in background is: @@ -592,7 +592,7 @@ The format for running a command in background is: If the shell is not interactive, the standard input of an asynchronous command is set to .Pa /dev/null . -.Ss Lists -- Generally Speaking +.Ss Lists \(en Generally Speaking A list is a sequence of zero or more commands separated by newlines, semicolons, or ampersands, and optionally terminated by one of these three characters. @@ -615,7 +615,7 @@ of the first command is nonzero. and .Dq || both have the same priority. -.Ss Flow-Control Constructs -- if, while, for, case +.Ss Flow-Control Constructs \(en if, while, for, case The syntax of the if command is .Bd -literal -offset indent if list @@ -694,7 +694,7 @@ Builtin commands grouped into a (list) will not affect the current shell. The second form does not fork another shell so is slightly more efficient. Grouping commands together this way allows you to redirect their output as though they were one program: -.Pp +.\".Pp .Bd -literal -offset indent { printf \*q hello \*q ; printf \*q world\\n" ; } \*[Gt] greeting .Ed @@ -1177,13 +1177,14 @@ mechanism was used or because the argument is a single dash. The .Fl P option causes the physical directory structure to be used, that is, all -symbolic links are resolved to their respective values. The +symbolic links are resolved to their respective values. +The .Fl L option turns off the effect of any preceding .Fl P options. .It Xo echo Op Fl n -.Ar args... +.Ar args... .Xc Print the arguments on the standard output, separated by spaces. Unless the @@ -1191,13 +1192,15 @@ Unless the option is present, a newline is output following the arguments. .Pp If any of the following sequences of characters is encountered during -output, the sequence is not output. Instead, the specified action is +output, the sequence is not output. +Instead, the specified action is performed: .Bl -tag -width indent .It Li \eb A backspace character is output. .It Li \ec -Subsequent output is suppressed. This is normally used at the end of the +Subsequent output is suppressed. +This is normally used at the end of the last argument to suppress the trailing newline that .Ic echo would otherwise output. @@ -1417,7 +1420,7 @@ and and the option .Op c , which requires an argument. -.Pp +.\".Pp .Bd -literal -offset indent while getopts abc: f do @@ -1431,7 +1434,7 @@ shift `expr $OPTIND - 1` .Ed .Pp This code will accept any of the following as equivalent: -.Pp +.\".Pp .Bd -literal -offset indent cmd \-acarg file file cmd \-a \-c arg file file @@ -1471,7 +1474,8 @@ will continue to print the old name for the directory. The .Fl P option causes the physical value of the current working directory to be shown, -that is, all symbolic links are resolved to their respective values. The +that is, all symbolic links are resolved to their respective values. +The .Fl L option turns off the effect of any preceding .Fl P @@ -1522,7 +1526,7 @@ variables. With the .Fl p option specified the output will be formatted suitably for non-interactive use. -.Pp +.\".Pp .It Xo printf Ar format .Op Ar arguments ... .Xc @@ -1780,9 +1784,11 @@ If options are given, it sets the specified option flags, or clears them as described in the section called .Sx Argument List Processing . As a special case, if the option is -o or +o and no argument is -supplied, the shell prints the settings of all its options. If the -option is -o, the settings are printed in a human-readable format; if -the option is +o, the settings are printed in a format suitable for +supplied, the shell prints the settings of all its options. +If the option is -o, +the settings are printed in a human-readable format; +if the option is +o, +the settings are printed in a format suitable for reinput to the shell to affect the same option settings. .Pp The third use of the set command is to set the values of the shell's @@ -2058,7 +2064,8 @@ operator has higher precedence than the operator. .It times Print the accumulated user and system times for the shell and for processes -run from the shell. The return status is 0. +run from the shell. +The return status is 0. .It Xo trap .Op Ar action Ar signal ... .Xc @@ -2179,7 +2186,7 @@ the current limit is displayed. Limits of an arbitrary process can be displayed or set using the .Xr sysctl 8 utility. -.Pp +.\".Pp .It umask Op Ar mask Set the value of umask (see .Xr umask 2 ) @@ -2308,11 +2315,13 @@ children of the shell, and is used in the history editing modes. .It Ev HISTSIZE The number of lines in the history buffer for the shell. .It Ev PWD -The logical value of the current working directory. This is set by the +The logical value of the current working directory. +This is set by the .Ic cd command. .It Ev OLDPWD -The previous logical value of the current working directory. This is set by +The previous logical value of the current working directory. +This is set by the .Ic cd command. @@ -2320,7 +2329,7 @@ command. The process ID of the parent process of the shell. .El .Sh FILES -.Bl -item -width HOMEprofilexxxx +.Bl -item .It .Pa $HOME/.profile .It diff --git a/src/error.h b/src/error.h index 94e30a2..661a8a0 100644 --- a/src/error.h +++ b/src/error.h @@ -116,11 +116,7 @@ void __inton(void); #define int_pending() intpending void exraise(int) __attribute__((__noreturn__)); -#ifdef USE_NORETURN void onint(void) __attribute__((__noreturn__)); -#else -void onint(void); -#endif extern int errlinno; void sh_error(const char *, ...) __attribute__((__noreturn__)); void exerror(int, const char *, ...) __attribute__((__noreturn__)); diff --git a/src/eval.c b/src/eval.c index d4190f9..4f2f8a5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -78,6 +78,9 @@ int exitstatus; /* exit status of last command */ int back_exitstatus; /* exit status of backquoted command */ int savestatus = -1; /* exit status of last command outside traps */ +/* Prevent PS4 nesting. */ +MKINIT int inps4; + #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) STATIC @@ -123,6 +126,7 @@ EXITRESET { } evalskip = 0; loopnest = 0; + inps4 = 0; } #endif @@ -209,6 +213,9 @@ evaltree(union node *n, int flags) setstackmark(&smark); + if (nflag) + goto out; + if (n == NULL) { TRACE(("evaltree(NULL) called\n")); goto out; @@ -231,23 +238,28 @@ evaltree(union node *n, int flags) break; #endif case NNOT: - status = !evaltree(n->nnot.com, EV_TESTED); - goto setstatus; + status = evaltree(n->nnot.com, EV_TESTED); + if (!evalskip) + status = !status; + break; case NREDIR: errlinno = lineno = n->nredir.linno; if (funcline) lineno -= funcline - 1; expredir(n->nredir.redirect); pushredir(n->nredir.redirect); - status = redirectsafe(n->nredir.redirect, REDIR_PUSH) ?: - evaltree(n->nredir.n, flags & EV_TESTED); + status = redirectsafe(n->nredir.redirect, REDIR_PUSH); + if (status) + checkexit = EV_TESTED; + else + status = evaltree(n->nredir.n, flags & EV_TESTED); if (n->nredir.redirect) popredir(0); - goto setstatus; + break; case NCMD: evalfn = evalcommand; checkexit: - checkexit = ~flags & EV_TESTED; + checkexit = EV_TESTED; goto calleval; case NFOR: evalfn = evalfor; @@ -285,7 +297,7 @@ evaltree(union node *n, int flags) evalfn = evaltree; calleval: status = evalfn(n, flags); - goto setstatus; + break; case NIF: status = evaltree(n->nif.test, EV_TESTED); if (evalskip) @@ -298,17 +310,19 @@ evaltree(union node *n, int flags) goto evaln; } status = 0; - goto setstatus; + break; case NDEFUN: defun(n); -setstatus: - exitstatus = status; break; } + + exitstatus = status; + out: dotrap(); - if (eflag && checkexit && status) + if (eflag && (~flags & checkexit) && status) + goto exexit; if (flags & EV_EXIT) { @@ -847,12 +861,14 @@ evalcommand(union node *cmd, int flags) } /* Print the command if xflag is set. */ - if (xflag) { + if (xflag && !inps4) { struct output *out; int sep; out = &preverrout; + inps4 = 1; outstr(expandstr(ps4val()), out); + inps4 = 0; sep = 0; sep = eprintlist(out, varlist.list, sep); eprintlist(out, osp, sep); diff --git a/src/expand.c b/src/expand.c index 1730670..2ed02d6 100644 --- a/src/expand.c +++ b/src/expand.c @@ -120,7 +120,7 @@ static size_t memtodest(const char *p, size_t len, int flags); STATIC ssize_t varvalue(char *, int, int, int); STATIC void expandmeta(struct strlist *); #ifdef HAVE_GLOB -STATIC void addglob(const glob_t *); +static void addglob(const glob64_t *); #else STATIC void expmeta(char *, unsigned, unsigned); STATIC struct strlist *expsort(struct strlist *); @@ -135,8 +135,6 @@ STATIC int pmatch(const char *, const char *); #endif static size_t cvtnum(intmax_t num, int flags); STATIC size_t esclen(const char *, const char *); -STATIC char *scanleft(char *, char *, char *, char *, int, int); -STATIC char *scanright(char *, char *, char *, char *, int, int); STATIC void varunset(const char *, const char *, const char *, int) __attribute__((__noreturn__)); @@ -541,10 +539,8 @@ expbackq(union node *cmd, int flag) } -STATIC char * -scanleft( - char *startp, char *rmesc, char *rmescend, char *str, int quotes, - int zero +static char *scanleft(char *startp, char *endp, char *rmesc, char *rmescend, + char *str, int quotes, int zero ) { char *loc; char *loc2; @@ -573,16 +569,14 @@ scanleft( } -STATIC char * -scanright( - char *startp, char *rmesc, char *rmescend, char *str, int quotes, - int zero +static char *scanright(char *startp, char *endp, char *rmesc, char *rmescend, + char *str, int quotes, int zero ) { int esc = 0; char *loc; char *loc2; - for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) { + for (loc = endp, loc2 = rmescend; loc >= startp; loc2--) { int match; char c = *loc2; const char *s = loc2; @@ -618,7 +612,9 @@ static char *subevalvar(char *start, char *str, int strloc, int startloc, long amount; char *rmesc, *rmescend; int zero; - char *(*scan)(char *, char *, char *, char *, int , int); + char *(*scan)(char *, char *, char *, char *, char *, int , int); + int nstrloc = strloc; + char *endp; char *p; p = argstr(start, (flag & EXP_DISCARD) | EXP_TILDE | @@ -646,33 +642,40 @@ static char *subevalvar(char *start, char *str, int strloc, int startloc, abort(); #endif - rmesc = startp; rmescend = stackblock() + strloc; + str = preglob(rmescend, FNMATCH_IS_ENABLED ? + RMESCAPE_ALLOC | RMESCAPE_GROW : 0); + if (FNMATCH_IS_ENABLED) { + startp = stackblock() + startloc; + rmescend = stackblock() + strloc; + nstrloc = str - (char *)stackblock(); + } + + rmesc = startp; if (quotes) { rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); - if (rmesc != startp) { + if (rmesc != startp) rmescend = expdest; - startp = stackblock() + startloc; - } + startp = stackblock() + startloc; + str = stackblock() + nstrloc; } rmescend--; - str = stackblock() + strloc; - preglob(str, 0); /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ zero = subtype >> 1; /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ scan = (subtype & 1) ^ zero ? scanleft : scanright; - loc = scan(startp, rmesc, rmescend, str, quotes, zero); + endp = stackblock() + strloc - 1; + loc = scan(startp, endp, rmesc, rmescend, str, quotes, zero); if (loc) { if (zero) { - memmove(startp, loc, str - loc); - loc = startp + (str - loc) - 1; + memmove(startp, loc, endp - loc); + loc = startp + (endp - loc); } *loc = '\0'; } else - loc = str - 1; + loc = endp; out: amount = loc - expdest; @@ -701,7 +704,7 @@ evalvar(char *p, int flag) int discard; int quoted; - varflags = *p++; + varflags = *p++ & ~VSBIT; subtype = varflags & VSTYPE; quoted = flag & EXP_QUOTED; @@ -1154,6 +1157,20 @@ void ifsfree(void) */ #ifdef HAVE_GLOB +#ifdef __GLIBC__ +void *opendir_interruptible(const char *pathname) +{ + if (int_pending()) { + suppressint = 0; + onint(); + } + + return opendir(pathname); +} +#else +#define GLOB_ALTDIRFUNC 0 +#endif + STATIC void expandmeta(struct strlist *str) { @@ -1161,14 +1178,23 @@ expandmeta(struct strlist *str) while (str) { const char *p; - glob_t pglob; + glob64_t pglob; int i; if (fflag) goto nometa; + +#ifdef __GLIBC__ + pglob.gl_closedir = (void *)closedir; + pglob.gl_readdir = (void *)readdir64; + pglob.gl_opendir = opendir_interruptible; + pglob.gl_lstat = lstat64; + pglob.gl_stat = stat64; +#endif + INTOFF; p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); - i = glob(p, GLOB_NOMAGIC, 0, &pglob); + i = glob64(p, GLOB_ALTDIRFUNC | GLOB_NOMAGIC, 0, &pglob); if (p != str->text) ckfree(p); switch (i) { @@ -1177,12 +1203,12 @@ expandmeta(struct strlist *str) (GLOB_NOMAGIC | GLOB_NOCHECK)) goto nometa2; addglob(&pglob); - globfree(&pglob); + globfree64(&pglob); INTON; break; case GLOB_NOMATCH: nometa2: - globfree(&pglob); + globfree64(&pglob); INTON; nometa: *exparg.lastp = str; @@ -1201,9 +1227,7 @@ expandmeta(struct strlist *str) * Add the result of glob(3) to the list. */ -STATIC void -addglob(pglob) - const glob_t *pglob; +static void addglob(const glob64_t *pglob) { char **p = pglob->gl_pathv; @@ -1480,7 +1504,9 @@ msort(struct strlist *list, int len) STATIC inline int patmatch(char *pattern, const char *string) { - return pmatch(preglob(pattern, 0), string); + return pmatch(preglob(pattern, FNMATCH_IS_ENABLED ? + RMESCAPE_ALLOC | RMESCAPE_GROW : 0), + string); } @@ -1633,15 +1659,22 @@ _rmescapes(char *str, int flag) int notescaped; int globbing; - p = strpbrk(str, qchars); + p = strpbrk(str, cqchars); if (!p) { return str; } q = p; r = str; + globbing = flag & RMESCAPE_GLOB; + if (flag & RMESCAPE_ALLOC) { size_t len = p - str; - size_t fulllen = len + strlen(p) + 1; + size_t fulllen = strlen(p); + + if (FNMATCH_IS_ENABLED && globbing) + fulllen *= 2; + + fulllen += len + 1; if (flag & RMESCAPE_GROW) { int strloc = str - (char *)stackblock(); @@ -1659,7 +1692,6 @@ _rmescapes(char *str, int flag) q = mempcpy(q, str, len); } } - globbing = flag & RMESCAPE_GLOB; notescaped = globbing; while (*p) { if (*p == (char)CTLQUOTEMARK) { @@ -1672,8 +1704,11 @@ _rmescapes(char *str, int flag) notescaped = 0; goto copy; } + if (FNMATCH_IS_ENABLED && *p == '^') + goto add_escape; if (*p == (char)CTLESC) { p++; +add_escape: if (notescaped) *q++ = '\\'; } @@ -1742,6 +1777,16 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) sh_error("%.*s: %s%s", end - var - 1, var, msg, tail); } +void restore_handler_expandarg(struct jmploc *savehandler, int err) +{ + handler = savehandler; + if (err) { + if (exception != EXERROR) + longjmp(handler->loc, 1); + ifsfree(); + } +} + #ifdef mkinit INCLUDE "expand.h" diff --git a/src/expand.h b/src/expand.h index c44b848..49a18f9 100644 --- a/src/expand.h +++ b/src/expand.h @@ -62,7 +62,9 @@ struct arglist { #define EXP_DISCARD 0x400 /* discard result of expansion */ +struct jmploc; union node; + void expandarg(union node *, struct arglist *, int); #define rmescapes(p) _rmescapes((p), 0) char *_rmescapes(char *, int); @@ -71,6 +73,7 @@ void recordregion(int, int, int); void removerecordregions(int); void ifsbreakup(char *, int, struct arglist *); void ifsfree(void); +void restore_handler_expandarg(struct jmploc *savehandler, int err); /* From arith.y */ intmax_t arith(const char *); diff --git a/src/input.c b/src/input.c index 4edb744..9feed62 100644 --- a/src/input.c +++ b/src/input.c @@ -58,7 +58,6 @@ #include "myhistedit.h" #endif -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ #define IBUFSIZ (BUFSIZ + 1) @@ -68,6 +67,7 @@ struct parsefile *parsefile = &basepf; /* current input file */ int whichprompt; /* 1 == PS1, 2 == PS2 */ STATIC void pushfile(void); +static void popstring(void); static int preadfd(void); /* static void setinputfd(int fd, int push); @@ -88,6 +88,7 @@ INIT { RESET { /* clear input buffer */ basepf.lleft = basepf.nleft = 0; + basepf.unget = 0; popallfiles(); } @@ -101,13 +102,32 @@ FORKRESET { #endif -/* - * Read a character from the script, returning PEOF on end of file. - * Nul characters in the input are silently discarded. - */ +static void freestrings(struct strpush *sp) +{ + INTOFF; + do { + struct strpush *psp; -int -pgetc(void) + if (sp->ap) { + sp->ap->flag &= ~ALIASINUSE; + if (sp->ap->flag & ALIASDEAD) { + unalias(sp->ap->name); + } + } + + psp = sp; + sp = sp->spfree; + + if (psp != &(parsefile->basestrpush)) + ckfree(psp); + } while (sp); + + parsefile->spfree = NULL; + INTON; +} + + +static int __pgetc(void) { int c; @@ -127,17 +147,18 @@ pgetc(void) /* - * Same as pgetc(), but ignores PEOA. + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. */ -int -pgetc2() +int pgetc(void) { - int c; - do { - c = pgetc(); - } while (c == PEOA); - return c; + struct strpush *sp = parsefile->spfree; + + if (unlikely(sp)) + freestrings(sp); + + return __pgetc(); } @@ -200,9 +221,8 @@ preadfd(void) * Refill the input buffer and return the next input character: * * 1) If a string was pushed back on the input, pop it; - * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading - * from a string so we can't refill the buffer, return EOF. - * 3) If the is more stuff in this buffer, use it else call read to fill it. + * 2) If we are reading from a string we can't refill the buffer, return EOF. + * 3) If there is more stuff in this buffer, use it else call read to fill it. * 4) Process input up to the next newline, deleting nul characters. */ @@ -216,19 +236,10 @@ static int preadbuffer(void) char savec; if (unlikely(parsefile->strpush)) { - if ( - parsefile->nleft == -1 && - parsefile->strpush->ap && - parsefile->nextc[-1] != ' ' && - parsefile->nextc[-1] != '\t' - ) { - return PEOA; - } popstring(); - return pgetc(); + return __pgetc(); } - if (unlikely(parsefile->nleft == EOF_NLEFT || - parsefile->buf == NULL)) + if (parsefile->buf == NULL) return PEOF; flushall(); @@ -236,7 +247,7 @@ static int preadbuffer(void) if (more <= 0) { again: if ((more = preadfd()) <= 0) { - parsefile->lleft = parsefile->nleft = EOF_NLEFT; + parsefile->lleft = parsefile->nleft = 0; return PEOF; } } @@ -333,7 +344,8 @@ pushstring(char *s, void *ap) len = strlen(s); INTOFF; /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ - if (parsefile->strpush) { + if ((unsigned long)parsefile->strpush | + (unsigned long)parsefile->spfree) { sp = ckmalloc(sizeof (struct strpush)); sp->prev = parsefile->strpush; parsefile->strpush = sp; @@ -342,6 +354,7 @@ pushstring(char *s, void *ap) sp->prevstring = parsefile->nextc; sp->prevnleft = parsefile->nleft; sp->unget = parsefile->unget; + sp->spfree = parsefile->spfree; memcpy(sp->lastc, parsefile->lastc, sizeof(sp->lastc)); sp->ap = (struct alias *)ap; if (ap) { @@ -351,11 +364,11 @@ pushstring(char *s, void *ap) parsefile->nextc = s; parsefile->nleft = len; parsefile->unget = 0; + parsefile->spfree = NULL; INTON; } -void -popstring(void) +static void popstring(void) { struct strpush *sp = parsefile->strpush; @@ -368,10 +381,6 @@ popstring(void) if (sp->string != sp->ap->val) { ckfree(sp->string); } - sp->ap->flag &= ~ALIASINUSE; - if (sp->ap->flag & ALIASDEAD) { - unalias(sp->ap->name); - } } parsefile->nextc = sp->prevstring; parsefile->nleft = sp->prevnleft; @@ -379,8 +388,7 @@ popstring(void) memcpy(parsefile->lastc, sp->lastc, sizeof(sp->lastc)); /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ parsefile->strpush = sp->prev; - if (sp != &(parsefile->basestrpush)) - ckfree(sp); + parsefile->spfree = sp; INTON; } @@ -395,12 +403,9 @@ setinputfile(const char *fname, int flags) int fd; INTOFF; - if ((fd = open64(fname, O_RDONLY)) < 0) { - if (flags & INPUT_NOFILE_OK) - goto out; - exitstatus = 127; - exerror(EXERROR, "Can't open %s", fname); - } + fd = sh_open(fname, O_RDONLY, flags & INPUT_NOFILE_OK); + if (fd < 0) + goto out; if (fd < 10) fd = savefd(fd, fd); setinputfd(fd, flags & INPUT_PUSH_FILE); @@ -465,6 +470,7 @@ pushfile(void) pf->prev = parsefile; pf->fd = -1; pf->strpush = NULL; + pf->spfree = NULL; pf->basestrpush.prev = NULL; pf->unget = 0; parsefile = pf; @@ -481,8 +487,12 @@ popfile(void) close(pf->fd); if (pf->buf) ckfree(pf->buf); - while (pf->strpush) + if (parsefile->spfree) + freestrings(parsefile->spfree); + while (pf->strpush) { popstring(); + freestrings(parsefile->spfree); + } parsefile = pf->prev; ckfree(pf); INTON; diff --git a/src/input.h b/src/input.h index afc31ef..826a6c4 100644 --- a/src/input.h +++ b/src/input.h @@ -50,6 +50,9 @@ struct strpush { struct alias *ap; /* if push was associated with an alias */ char *string; /* remember the string since it may change */ + /* Delay freeing so we can stop nested aliases. */ + struct strpush *spfree; + /* Remember last two characters for pungetc. */ int lastc[2]; @@ -73,6 +76,9 @@ struct parsefile { struct strpush *strpush; /* for pushing strings at this level */ struct strpush basestrpush; /* so pushing one is fast */ + /* Delay freeing so we can stop nested aliases. */ + struct strpush *spfree; + /* Remember last two characters for pungetc. */ int lastc[2]; @@ -93,7 +99,6 @@ int pgetc(void); int pgetc2(void); void pungetc(void); void pushstring(char *, void *); -void popstring(void); int setinputfile(const char *, int); void setinputfd(int fd, int push); // libdash void setinputstring(char *); diff --git a/src/jobs.c b/src/jobs.c index f30313b..f3b9ffc 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -197,7 +197,7 @@ setjobctl(int on) return; if (on) { int ofd; - ofd = fd = open64(_PATH_TTY, O_RDWR); + ofd = fd = sh_open(_PATH_TTY, O_RDWR, 1); if (fd < 0) { fd += 3; while (!isatty(fd)) @@ -888,15 +888,12 @@ static void forkchild(struct job *jp, union node *n, int mode) ignoresig(SIGQUIT); if (jp->nprocs == 0) { close(0); - if (open64(_PATH_DEVNULL, O_RDONLY) != 0) - sh_error("Can't open %s", _PATH_DEVNULL); + sh_open(_PATH_DEVNULL, O_RDONLY, 0); } } if (!oldlvl && iflag) { - if (mode != FORK_BG) { - setsignal(SIGINT); - setsignal(SIGQUIT); - } + setsignal(SIGINT); + setsignal(SIGQUIT); setsignal(SIGTERM); } @@ -1513,7 +1510,13 @@ showpipe(struct job *jp, struct output *out) STATIC void xtcsetpgrp(int fd, pid_t pgrp) { - if (tcsetpgrp(fd, pgrp)) + int err; + + sigblockall(NULL); + err = tcsetpgrp(fd, pgrp); + sigclearmask(); + + if (err) sh_error("Cannot set tty process group (%s)", strerror(errno)); } #endif diff --git a/src/main.c b/src/main.c index 5c35003..04bc1d4 100644 --- a/src/main.c +++ b/src/main.c @@ -247,7 +247,7 @@ cmdloop(int top) out2str("\nUse \"exit\" to leave shell.\n"); } numeof++; - } else if (nflag == 0) { + } else { int i; job_warning = (job_warning == 2) ? 1 : 0; diff --git a/src/mksyntax.c b/src/mksyntax.c index a23c18c..da18f5d 100644 --- a/src/mksyntax.c +++ b/src/mksyntax.c @@ -64,7 +64,6 @@ struct synclass synclass[] = { { "CEOF", "end of file" }, { "CCTL", "like CWORD, except it must be escaped" }, { "CSPCL", "these terminate a word" }, - { "CIGN", "character should be ignored" }, { NULL, NULL } }; @@ -145,9 +144,8 @@ main(int argc, char **argv) fprintf(hfile, "/* %s */\n", is_entry[i].comment); } putc('\n', hfile); - fprintf(hfile, "#define SYNBASE %d\n", 130); - fprintf(hfile, "#define PEOF %d\n\n", -130); - fprintf(hfile, "#define PEOA %d\n\n", -129); + fprintf(hfile, "#define SYNBASE %d\n", 129); + fprintf(hfile, "#define PEOF %d\n\n", -129); putc('\n', hfile); fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile); fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile); @@ -170,7 +168,6 @@ main(int argc, char **argv) add("$", "CVAR"); add("}", "CENDVAR"); add("<>();&| \t", "CSPCL"); - syntax[1] = "CSPCL"; print("basesyntax"); init(); fputs("\n/* syntax table used when in double quotes */\n", cfile); @@ -223,7 +220,7 @@ filltable(char *dftval) { int i; - for (i = 0 ; i < 258; i++) + for (i = 0 ; i < 257; i++) syntax[i] = dftval; } @@ -239,9 +236,8 @@ init(void) filltable("CWORD"); syntax[0] = "CEOF"; - syntax[1] = "CIGN"; for (ctl = CTL_FIRST; ctl <= CTL_LAST; ctl++ ) - syntax[130 + ctl] = "CCTL"; + syntax[129 + ctl] = "CCTL"; } @@ -253,7 +249,7 @@ static void add(char *p, char *type) { while (*p) - syntax[(signed char)*p++ + 130] = type; + syntax[(signed char)*p++ + 129] = type; } @@ -271,7 +267,7 @@ print(char *name) fprintf(hfile, "extern const char %s[];\n", name); fprintf(cfile, "const char %s[] = {\n", name); col = 0; - for (i = 0 ; i < 258; i++) { + for (i = 0 ; i < 257; i++) { if (i == 0) { fputs(" ", cfile); } else if ((i & 03) == 0) { diff --git a/src/mktokens b/src/mktokens index 9470ab1..78055be 100644 --- a/src/mktokens +++ b/src/mktokens @@ -95,4 +95,3 @@ sed 's/"//g' "${TMPDIR}"/ka$$ | awk ' END{print " \"" last "\"\n};"}' rm "${TMPDIR}"/ka$$ - diff --git a/src/mystring.c b/src/mystring.c index de624b8..f651521 100644 --- a/src/mystring.c +++ b/src/mystring.c @@ -60,9 +60,14 @@ char nullstr[1]; /* zero length string */ const char spcstr[] = " "; const char snlfmt[] = "%s\n"; -const char dolatstr[] = { CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', +const char dolatstr[] = { CTLQUOTEMARK, CTLVAR, VSNORMAL | VSBIT, '@', '=', CTLQUOTEMARK, '\0' }; -const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; +const char cqchars[] = { +#ifdef HAVE_FNMATCH + '^', +#endif + CTLESC, CTLQUOTEMARK, 0 +}; const char illnum[] = "Illegal number: %s"; const char homestr[] = "HOME"; diff --git a/src/mystring.h b/src/mystring.h index 083ea98..564b911 100644 --- a/src/mystring.h +++ b/src/mystring.h @@ -37,11 +37,18 @@ #include #include +#ifdef HAVE_FNMATCH +#define FNMATCH_IS_ENABLED 1 +#else +#define FNMATCH_IS_ENABLED 0 +#endif + extern const char snlfmt[]; extern const char spcstr[]; extern const char dolatstr[]; #define DOLATSTRLEN 6 -extern const char qchars[]; +extern const char cqchars[]; +#define qchars (cqchars + FNMATCH_IS_ENABLED) extern const char illnum[]; extern const char homestr[]; diff --git a/src/parser.c b/src/parser.c index a4f94fc..b0e806e 100644 --- a/src/parser.c +++ b/src/parser.c @@ -818,7 +818,6 @@ xxreadtoken(void) c = pgetc_eatbnl(); switch (c) { case ' ': case '\t': - case PEOA: continue; case '#': while ((c = pgetc()) != '\n' && c != PEOF); @@ -860,7 +859,7 @@ static int pgetc_eatbnl(void) int c; while ((c = pgetc()) == '\\') { - if (pgetc2() != '\n') { + if (pgetc() != '\n') { pungetc(); break; } @@ -965,7 +964,7 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) break; /* backslash */ case CBACK: - c = pgetc2(); + c = pgetc(); if (c == PEOF) { USTPUTC(CTLESC, out); USTPUTC('\\', out); @@ -1070,14 +1069,10 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) break; case CEOF: goto endword; /* exit outer loop */ - case CIGN: - break; default: if (synstack->varnest == 0) goto endword; /* exit outer loop */ - if (c != PEOA) { - USTPUTC(c, out); - } + USTPUTC(c, out); } c = pgetc_top(synstack); } @@ -1125,13 +1120,9 @@ checkend: { int markloc; char *p; - if (c == PEOA) { - c = pgetc2(); - } if (striptabs) { - while (c == '\t') { - c = pgetc2(); - } + while (c == '\t') + c = pgetc(); } markloc = out - (char *)stackblock(); @@ -1139,7 +1130,7 @@ checkend: { if (c != *p) goto more_heredoc; - c = pgetc2(); + c = pgetc(); } if (c == '\n' || c == PEOF) { @@ -1251,7 +1242,6 @@ parsesub: { c = pgetc_eatbnl(); if ( (checkkwd & CHKEOFMARK) || - c <= PEOA || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) ) { USTPUTC('$', out); @@ -1365,7 +1355,7 @@ parsesub: { synstack->dblquote = newsyn != BASESYNTAX; } - *((char *)stackblock() + typeloc) = subtype; + *((char *)stackblock() + typeloc) = subtype | VSBIT; if (subtype != VSNORMAL) { synstack->varnest++; if (synstack->dblquote) @@ -1422,13 +1412,9 @@ parsebackq: { if (pc != '\\' && pc != '`' && pc != '$' && (!synstack->dblquote || pc != '"')) STPUTC('\\', pout); - if (pc > PEOA) { - break; - } - /* fall through */ + break; case PEOF: - case PEOA: synerror("EOF in backquote substitution"); case '\n': @@ -1625,9 +1611,7 @@ expandstr(const char *ps) result = stackblock(); out: - handler = savehandler; - if (err && exception != EXERROR) - longjmp(handler->loc, 1); + restore_handler_expandarg(savehandler, err); doprompt = saveprompt; unwindfiles(file_stop); diff --git a/src/parser.h b/src/parser.h index 9f1d3ad..13b5829 100644 --- a/src/parser.h +++ b/src/parser.h @@ -50,6 +50,7 @@ /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ #define VSNUL 0x10 /* colon--treat the empty string as unset */ +#define VSBIT 0x20 /* Ensure subtype is not zero */ /* values of VSTYPE field */ #define VSNORMAL 0x1 /* normal variable: $var or ${var} */ diff --git a/src/redir.c b/src/redir.c index 2f1d73c..8b61736 100644 --- a/src/redir.c +++ b/src/redir.c @@ -55,6 +55,7 @@ #include "output.h" #include "memalloc.h" #include "error.h" +#include "trap.h" #define EMPTY -2 /* marks an unused slot in redirtab */ @@ -180,56 +181,83 @@ redirect(union node *redir, int flags) } +static int sh_open_fail(const char *, int, int) __attribute__((__noreturn__)); +static int sh_open_fail(const char *pathname, int flags, int e) +{ + const char *word; + int action; + + word = "open"; + action = E_OPEN; + if (flags & O_CREAT) { + word = "create"; + action = E_CREAT; + } + + sh_error("cannot %s %s: %s", word, pathname, errmsg(e, action)); +} + + +int sh_open(const char *pathname, int flags, int mayfail) +{ + int fd; + int e; + + do { + fd = open64(pathname, flags, 0666); + e = errno; + } while (fd < 0 && e == EINTR && !pending_sig); + + if (mayfail || fd >= 0) + return fd; + + sh_open_fail(pathname, flags, e); +} + + STATIC int openredirect(union node *redir) { struct stat64 sb; char *fname; + int flags; int f; switch (redir->nfile.type) { case NFROM: - fname = redir->nfile.expfname; - if ((f = open64(fname, O_RDONLY)) < 0) - goto eopen; + flags = O_RDONLY; +do_open: + f = sh_open(redir->nfile.expfname, flags, 0); break; case NFROMTO: - fname = redir->nfile.expfname; - if ((f = open64(fname, O_RDWR|O_CREAT, 0666)) < 0) - goto ecreate; - break; + flags = O_RDWR|O_CREAT; + goto do_open; case NTO: /* Take care of noclobber mode. */ if (Cflag) { fname = redir->nfile.expfname; if (stat64(fname, &sb) < 0) { - if ((f = open64(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) - goto ecreate; - } else if (!S_ISREG(sb.st_mode)) { - if ((f = open64(fname, O_WRONLY, 0666)) < 0) - goto ecreate; - if (!fstat64(f, &sb) && S_ISREG(sb.st_mode)) { - close(f); - errno = EEXIST; - goto ecreate; - } - } else { - errno = EEXIST; + flags = O_WRONLY|O_CREAT|O_EXCL; + goto do_open; + } + + if (S_ISREG(sb.st_mode)) + goto ecreate; + + f = sh_open(fname, O_WRONLY, 0); + if (!fstat64(f, &sb) && S_ISREG(sb.st_mode)) { + close(f); goto ecreate; } break; } /* FALLTHROUGH */ case NCLOBBER: - fname = redir->nfile.expfname; - if ((f = open64(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) - goto ecreate; - break; + flags = O_WRONLY|O_CREAT|O_TRUNC; + goto do_open; case NAPPEND: - fname = redir->nfile.expfname; - if ((f = open64(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) - goto ecreate; - break; + flags = O_WRONLY|O_CREAT|O_APPEND; + goto do_open; case NTOFD: case NFROMFD: f = redir->ndup.dupfd; @@ -249,9 +277,7 @@ openredirect(union node *redir) return f; ecreate: - sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); -eopen: - sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); + sh_open_fail(fname, O_CREAT, EEXIST); } @@ -462,16 +488,14 @@ redirectsafe(union node *redir, int flags) struct jmploc *volatile savehandler = handler; struct jmploc jmploc; - SAVEINT(saveint); - if (!(err = setjmp(jmploc.loc) * 2)) { - handler = &jmploc; - redirect(redir, flags); - } - handler = savehandler; - if (err && exception != EXERROR) - longjmp(handler->loc, 1); - RESTOREINT(saveint); - return err; + SAVEINT(saveint); + if (!(err = setjmp(jmploc.loc) * 2)) { + handler = &jmploc; + redirect(redir, flags); + } + restore_handler_expandarg(savehandler, err); + RESTOREINT(saveint); + return err; } diff --git a/src/redir.h b/src/redir.h index 6e9485e..444a635 100644 --- a/src/redir.h +++ b/src/redir.h @@ -50,4 +50,5 @@ int savefd(int, int); int redirectsafe(union node *, int); void unwindredir(struct redirtab *stop); struct redirtab *pushredir(union node *redir); +int sh_open(const char *pathname, int flags, int mayfail);