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);