Index: lib/libedit/read.h =================================================================== --- lib/libedit/read.h (revision 209146) +++ lib/libedit/read.h (working copy) @@ -13,9 +13,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED Index: lib/libedit/Makefile =================================================================== --- lib/libedit/Makefile (revision 209146) +++ lib/libedit/Makefile (working copy) @@ -6,7 +6,8 @@ SHLIB_MAJOR= 7 SHLIBDIR?= /lib -OSRCS= chared.c common.c el.c emacs.c fcns.c help.c hist.c key.c map.c \ +OSRCS= chared.c common.c el.c emacs.c fcns.c filecomplete.c help.c \ + hist.c key.c map.c \ parse.c prompt.c read.c refresh.c search.c sig.c term.c tty.c vi.c DPADD= ${LIBNCURSES} @@ -35,6 +36,8 @@ CFLAGS+= #-DDEBUG_TTY -DDEBUG_KEY -DDEBUG_READ -DDEBUG -DDEBUG_REFRESH CFLAGS+= #-DDEBUG_PASTE -DDEBUG_EDIT +WARNS?= 1 + AHDR= vi.h emacs.h common.h ASRC= ${.CURDIR}/vi.c ${.CURDIR}/emacs.c ${.CURDIR}/common.c Index: lib/libedit/editrc.5 =================================================================== --- lib/libedit/editrc.5 (revision 209146) +++ lib/libedit/editrc.5 (working copy) @@ -13,9 +13,6 @@ .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of The NetBSD Foundation nor the names of its -.\" contributors may be used to endorse or promote products derived -.\" from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -32,8 +29,8 @@ .\" $FreeBSD$ .\" .Dd October 18, 2003 -.Os .Dt EDITRC 5 +.Os .Sh NAME .Nm editrc .Nd configuration file for editline library Index: lib/libedit/editline.3 =================================================================== --- lib/libedit/editline.3 (revision 209146) +++ lib/libedit/editline.3 (working copy) @@ -13,9 +13,6 @@ .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of The NetBSD Foundation nor the names of its -.\" contributors may be used to endorse or promote products derived -.\" from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -32,8 +29,8 @@ .\" $FreeBSD$ .\" .Dd January 12, 2007 -.Os .Dt EDITLINE 3 +.Os .Sh NAME .Nm editline , .Nm el_init , Index: include/histedit.h =================================================================== --- include/histedit.h (revision 209146) +++ include/histedit.h (working copy) @@ -105,9 +105,8 @@ */ int el_set(EditLine *, int, ...); int el_get(EditLine *, int, ...); -#if 0 unsigned char _el_fn_complete(EditLine *, int); -#endif +unsigned char _el_fn_sh_complete(EditLine *, int); /* * el_set/el_get parameters Index: bin/sh/memalloc.h =================================================================== --- bin/sh/memalloc.h (revision 209146) +++ bin/sh/memalloc.h (working copy) @@ -51,7 +51,7 @@ pointer ckmalloc(size_t); pointer ckrealloc(pointer, int); void ckfree(pointer); -char *savestr(char *); +char *savestr(const char *); pointer stalloc(int); void stunalloc(pointer); void setstackmark(struct stackmark *); Index: bin/sh/alias.h =================================================================== --- bin/sh/alias.h (revision 209146) +++ bin/sh/alias.h (working copy) @@ -42,7 +42,7 @@ int flag; }; -struct alias *lookupalias(char *, int); +struct alias *lookupalias(const char *, int); int aliascmd(int, char **); int unaliascmd(int, char **); void rmaliases(void); Index: bin/sh/mkbuiltins =================================================================== --- bin/sh/mkbuiltins (revision 209146) +++ bin/sh/mkbuiltins (working copy) @@ -88,7 +88,7 @@ awk '{ printf "#define %s %d\n", $1, NR-1}' echo ' struct builtincmd { - char *name; + const char *name; int code; int special; }; Index: bin/sh/options.h =================================================================== --- bin/sh/options.h (revision 209146) +++ bin/sh/options.h (working copy) @@ -102,7 +102,7 @@ extern struct shparam shellparam; /* $@ */ extern char **argptr; /* argument list for builtin commands */ extern char *shoptarg; /* set by nextopt */ -extern char *optptr; /* used by nextopt */ +extern char *nextopt_optptr; /* used by nextopt */ void procargs(int, char **); void optschanged(void); @@ -111,5 +111,5 @@ int shiftcmd(int, char **); int setcmd(int, char **); int getoptscmd(int, char **); -int nextopt(char *); +int nextopt(const char *); void getoptsreset(const char *); Index: bin/sh/output.c =================================================================== --- bin/sh/output.c (revision 209146) +++ bin/sh/output.c (working copy) @@ -71,7 +71,7 @@ static int doformat_wr(void *, const char *, int); struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; -struct output errout = {NULL, 0, NULL, 100, 2, 0}; +struct output errout = {NULL, 0, NULL, 256, 2, 0}; struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; struct output *out1 = &output; struct output *out2 = &errout; @@ -124,8 +124,6 @@ { while (*p) outc(*p++, file); - if (file == out2) - flushout(file); } /* Like outstr(), but quote for re-input into the shell. */ @@ -255,7 +253,7 @@ } void -dprintf(const char *fmt, ...) +out2fmt_flush(const char *fmt, ...) { va_list ap; @@ -316,7 +314,7 @@ */ int -xwrite(int fd, char *buf, int nbytes) +xwrite(int fd, const char *buf, int nbytes) { int ntry; int i; Index: bin/sh/parser.h =================================================================== --- bin/sh/parser.h (revision 209146) +++ bin/sh/parser.h (working copy) @@ -73,9 +73,10 @@ extern int tokpushback; #define NEOF ((union node *)&tokpushback) extern int whichprompt; /* 1 == PS1, 2 == PS2 */ +extern const char *const parsekwd[]; union node *parsecmd(int); void fixredir(union node *, const char *, int); -int goodname(char *); +int goodname(const char *); char *getprompt(void *); Index: bin/sh/output.h =================================================================== --- bin/sh/output.h (revision 209146) +++ bin/sh/output.h (working copy) @@ -46,13 +46,13 @@ short flags; }; -extern struct output output; -extern struct output errout; +extern struct output output; /* to fd 1 */ +extern struct output errout; /* to fd 2 */ extern struct output memout; -extern struct output *out1; -extern struct output *out2; +extern struct output *out1; /* &memout if backquote, otherwise &output */ +extern struct output *out2; /* &memout if backquote with 2>&1, otherwise + &errout */ -void open_mem(char *, int, struct output *); void out1str(const char *); void out1qstr(const char *); void out2str(const char *); @@ -65,10 +65,10 @@ void freestdout(void); void outfmt(struct output *, const char *, ...) __printflike(2, 3); void out1fmt(const char *, ...) __printflike(1, 2); -void dprintf(const char *, ...) __printflike(1, 2); +void out2fmt_flush(const char *, ...) __printflike(1, 2); void fmtstr(char *, int, const char *, ...) __printflike(3, 4); void doformat(struct output *, const char *, va_list) __printflike(2, 0); -int xwrite(int, char *, int); +int xwrite(int, const char *, int); #define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) #define out1c(c) outc(c, out1); Index: bin/sh/mystring.c =================================================================== --- bin/sh/mystring.c (revision 209146) +++ bin/sh/mystring.c (working copy) @@ -108,7 +108,7 @@ number(const char *s) { if (! is_number(s)) - error("Illegal number: %s", (char *)s); + error("Illegal number: %s", s); return atoi(s); } Index: bin/sh/histedit.c =================================================================== --- bin/sh/histedit.c (revision 209146) +++ bin/sh/histedit.c (working copy) @@ -92,12 +92,14 @@ if (hist != NULL) sethistsize(histsizeval()); else - out2str("sh: can't initialize history\n"); + out2fmt_flush("sh: can't initialize history\n"); } if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ /* * turn editing on */ + char *term; + INTOFF; if (el_in == NULL) el_in = fdopen(0, "r"); @@ -107,14 +109,22 @@ el_out = fdopen(2, "w"); if (el_in == NULL || el_err == NULL || el_out == NULL) goto bad; + term = lookupvar("TERM"); + if (term) + setenv("TERM", term, 1); + else + unsetenv("TERM"); el = el_init(arg0, el_in, el_out, el_err); if (el != NULL) { if (hist) el_set(el, EL_HIST, history, hist); el_set(el, EL_PROMPT, getprompt); + el_set(el, EL_ADDFN, "sh-complete", + "Filename completion", + _el_fn_sh_complete); } else { bad: - out2str("sh: can't initialize editing\n"); + out2fmt_flush("sh: can't initialize editing\n"); } INTON; } else if (!editing && el) { @@ -128,6 +138,7 @@ el_set(el, EL_EDITOR, "vi"); else if (Eflag) el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_BIND, "^I", "sh-complete", NULL); el_source(el, NULL); } } else { @@ -160,23 +171,30 @@ } } +void +setterm(const char *term) +{ + if (rootshell && el != NULL && term != NULL) + el_set(el, EL_TERMINAL, term); +} + int histcmd(int argc, char **argv) { int ch; - char *editor = NULL; + const char *editor = NULL; HistEvent he; int lflg = 0, nflg = 0, rflg = 0, sflg = 0; int i, retval; - char *firststr, *laststr; + const char *firststr, *laststr; int first, last, direction; - char *pat = NULL, *repl; + char *pat = NULL, *repl = NULL; static int active = 0; struct jmploc jmploc; struct jmploc *savehandler; char editfilestr[PATH_MAX]; char *volatile editfile; - FILE *efp; + FILE *efp = NULL; int oldhistnum; if (hist == NULL) @@ -336,6 +354,7 @@ if (sflg) { if (displayhist) { out2str(s); + flushout(out2); } evalstring(s, 0); if (displayhist && hist) { @@ -405,7 +424,7 @@ } int -not_fcnumber(char *s) +not_fcnumber(const char *s) { if (s == NULL) return (0); @@ -415,10 +434,10 @@ } int -str_to_event(char *str, int last) +str_to_event(const char *str, int last) { HistEvent he; - char *s = str; + const char *s = str; int relative = 0; int i, retval; Index: bin/sh/show.c =================================================================== --- bin/sh/show.c (revision 209146) +++ bin/sh/show.c (working copy) @@ -307,7 +307,7 @@ void -trputs(char *s) +trputs(const char *s) { if (tracefile == NULL) return; Index: bin/sh/show.h =================================================================== --- bin/sh/show.h (revision 209146) +++ bin/sh/show.h (working copy) @@ -35,6 +35,6 @@ void sh_trace(const char *, ...) __printflike(1, 2); void trargs(char **); void trputc(int); -void trputs(char *); +void trputs(const char *); void opentrace(void); #endif Index: bin/sh/arith_lex.l =================================================================== --- bin/sh/arith_lex.l (revision 209146) +++ bin/sh/arith_lex.l (working copy) @@ -51,13 +51,6 @@ int yylex(void); -struct varname -{ - struct varname *next; - char name[1]; -}; -static struct varname *varnames; - #undef YY_INPUT #define YY_INPUT(buf,result,max) \ result = (*buf = *arith_buf++) ? 1 : YY_NULL; @@ -87,14 +80,11 @@ * If variable doesn't exist, we should initialize * it to zero. */ - struct varname *temp; + char *temp; if (lookupvar(yytext) == NULL) setvarsafe(yytext, "0", 0); - temp = ckmalloc(sizeof(struct varname) + - strlen(yytext)); - temp->next = varnames; - varnames = temp; - yylval.s_value = strcpy(temp->name, yytext); + temp = stalloc(strlen(yytext) + 1); + yylval.s_value = strcpy(temp, yytext); return ARITH_VAR; } @@ -140,15 +130,5 @@ void arith_lex_reset(void) { - struct varname *name, *next; - YY_NEW_FILE; - - name = varnames; - while (name != NULL) { - next = name->next; - ckfree(name); - name = next; - } - varnames = NULL; } Index: bin/sh/myhistedit.h =================================================================== --- bin/sh/myhistedit.h (revision 209146) +++ bin/sh/myhistedit.h (working copy) @@ -38,8 +38,9 @@ void histedit(void); void sethistsize(const char *); +void setterm(const char *); int histcmd(int, char **); -int not_fcnumber(char *); -int str_to_event(char *, int); +int not_fcnumber(const char *); +int str_to_event(const char *, int); int bindcmd(int, char **); Index: bin/sh/sh.1 =================================================================== --- bin/sh/sh.1 (revision 209146) +++ bin/sh/sh.1 (working copy) @@ -32,7 +32,7 @@ .\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" $FreeBSD$ .\" -.Dd January 8, 2010 +.Dd May 24, 2010 .Dt SH 1 .Os .Sh NAME @@ -210,7 +210,8 @@ .Xr emacs 1 command line editor (disables the .Fl V -option if it has been set). +option if it has been set; +set automatically when interactive on terminals). .It Fl e Li errexit Exit immediately if any untested command fails in non-interactive mode. The exit status of a command is considered to be @@ -296,7 +297,10 @@ .Ed .It Fl u Li nounset Write a message to standard error when attempting -to expand a variable that is not set, and if the +to expand a variable, a positional parameter or +the special parameter +.Va \&! +that is not set, and if the shell is not interactive, exit immediately. .It Fl V Li vi Enable the built-in @@ -411,11 +415,11 @@ .Pq Ql \en . A backslash preceding a newline is treated as a line continuation. .El -.Ss Reserved Words -Reserved words are words that have special meaning to the +.Ss Keywords +Keywords or reserved words are words that have special meaning to the shell and are recognized at the beginning of a line and after a control operator. -The following are reserved words: +The following are keywords: .Bl -column "doneXX" "elifXX" "elseXX" "untilXX" "whileX" -offset center .It Li \&! Ta { Ta } Ta Ic case Ta Ic do .It Ic done Ta Ic elif Ta Ic else Ta Ic esac Ta Ic fi @@ -425,8 +429,8 @@ An alias is a name and corresponding value set using the .Ic alias built-in command. -Whenever a reserved word may occur (see above), -and after checking for reserved words, the shell +Whenever a keyword may occur (see above), +and after checking for keywords, the shell checks the word to see if it matches an alias. If it does, it replaces it in the input stream with its value. For example, if there is an alias called @@ -465,7 +469,7 @@ document). Essentially though, a line is read and if the first word of the line (or after a control operator) -is not a reserved word, then the shell has recognized a +is not a keyword, then the shell has recognized a simple command. Otherwise, a complex command or some other special construct may have been recognized. @@ -661,11 +665,11 @@ performing any searches. .It The shell searches each entry in the -.Ev PATH -environment variable +.Va PATH +variable in turn for the command. The value of the -.Ev PATH +.Va PATH variable should be a series of entries separated by colons. Each entry consists of a @@ -691,7 +695,7 @@ .In sys/signal.h . .Ss Complex Commands Complex commands are combinations of simple commands -with control operators or reserved words, together creating a larger complex +with control operators or keywords, together creating a larger complex command. More generally, a command is one of the following: .Bl -item -offset indent @@ -735,7 +739,7 @@ If the pipeline is not in the background (discussed later), the shell waits for all commands to complete. .Pp -If the reserved word +If the keyword .Ic !\& does not precede the pipeline, the exit status is the exit status of the last command specified @@ -1036,13 +1040,9 @@ .Ic set built-in command can also be used to set or reset them. .Ss Special Parameters -A special parameter is a parameter denoted by a special one-character -name. -The special parameters recognized by the -.Nm -shell of -.Fx -are shown in the following list, exactly as they would appear in input +Special parameters are parameters denoted by a single special character +or the digit zero. +They are shown in the following list, exactly as they would appear in input typed by the user or in the source of a shell script. .Bl -hang .It Li $* @@ -1109,6 +1109,84 @@ .It Li $0 (zero) Expands to the name of the shell or shell script. .El +.Ss Special Variables +The following variables are set by the shell or +have special meaning to it: +.Bl -tag -width ".Va HISTSIZE" +.It Va CDPATH +The search path used with the +.Ic cd +built-in. +.It Va EDITOR +The fallback editor used with the +.Ic fc +built-in. +If not set, the default editor is +.Xr ed 1 . +.It Va FCEDIT +The default editor used with the +.Ic fc +built-in. +.It Va HISTSIZE +The number of previous commands that are accessible. +.It Va HOME +The user's home directory, +used in tilde expansion and as a default directory for the +.Ic cd +built-in. +.It Va IFS +Input Field Separators. +This is normally set to +.Aq space , +.Aq tab , +and +.Aq newline . +See the +.Sx White Space Splitting +section for more details. +.It Va LINENO +The current line number in the script or function. +.It Va MAIL +The name of a mail file, that will be checked for the arrival of new +mail. +Overridden by +.Va MAILPATH . +.It Va MAILPATH +A colon +.Pq Ql \&: +separated list of file names, for the shell to check for incoming +mail. +This variable overrides the +.Va MAIL +setting. +There is a maximum of 10 mailboxes that can be monitored at once. +.It Va PATH +The default search path for executables. +See the +.Sx Path Search +section for details. +.It Va PPID +The parent process ID of the invoked shell. +This is set at startup +unless this variable is in the environment. +A later change of parent process ID is not reflected. +A subshell retains the same value of +.Va PPID . +.It Va PS1 +The primary prompt string, which defaults to +.Dq Li "$ " , +unless you are the superuser, in which case it defaults to +.Dq Li "# " . +.It Va PS2 +The secondary prompt string, which defaults to +.Dq Li "> " . +.It Va PS4 +The prefix for the trace output (if +.Fl x +is active). +The default is +.Dq Li "+ " . +.El .Ss Word Expansions This clause describes the various expansions that are performed on words. @@ -1162,7 +1240,7 @@ username is missing (as in .Pa ~/foobar ) , the tilde is replaced with the value of the -.Ev HOME +.Va HOME variable (the current user's home directory). .Ss Parameter Expansion The format for parameter expansion is as follows: @@ -1175,10 +1253,20 @@ .Ql } . Any .Ql } -escaped by a backslash or within a quoted string, and characters in +escaped by a backslash or within a single-quoted string, and characters in embedded arithmetic expansions, command substitutions, and variable expansions, are not examined in determining the matching .Ql } . +Except for the variants with +.Ql + , +.Ql - , +.Ql = +or +.Ql ?\& , +any +.Ql } +within a double-quoted string is also not examined in determining the matching +.Ql } . .Pp The simplest form for parameter expansion is: .Pp @@ -1262,6 +1350,14 @@ In the parameter expansions shown previously, use of the colon in the format results in a test for a parameter that is unset or null; omission of the colon results in a test for a parameter that is only unset. +.Pp +The +.Ar word +inherits the type of quoting +(unquoted, double-quoted or here-document) +from the surroundings, +with the exception that a backslash that quotes a closing brace is removed +during quote removal. .Bl -tag -width indent .It Li ${# Ns Ar parameter Ns Li } String Length. @@ -1469,10 +1565,10 @@ .Ql / characters, it is used as is. Otherwise, the shell searches the -.Ev PATH +.Va PATH for the file. If it is not found in the -.Ev PATH , +.Va PATH , it is sought in the current working directory. .It Ic \&[ A built-in equivalent of @@ -1516,7 +1612,7 @@ Switch to the specified .Ar directory , or to the directory specified in the -.Ev HOME +.Va HOME environment variable if no .Ar directory is specified. @@ -1527,17 +1623,17 @@ or .Pa .. , then the directories listed in the -.Ev CDPATH +.Va CDPATH variable will be searched for the specified .Ar directory . If -.Ev CDPATH +.Va CDPATH is unset, the current directory is searched. The format of -.Ev CDPATH +.Va CDPATH is the same as that of -.Ev PATH . +.Va PATH . In an interactive shell, the .Ic cd @@ -1545,7 +1641,7 @@ that it actually switched to if this is different from the name that the user gave. These may be different either because the -.Ev CDPATH +.Va CDPATH mechanism was used or because a symbolic link was crossed. .Pp If the @@ -1568,16 +1664,18 @@ .It Ic command Oo Fl p Oc Op Ar utility Op Ar argument ... .It Ic command Oo Fl v | V Oc Op Ar utility The first form of invocation executes the specified +.Ar utility , +ignoring shell functions in the search. +If .Ar utility -as a simple command (see the -.Sx Simple Commands -section). +is a special builtin, +it is executed as if it were a regular builtin. .Pp If the .Fl p option is specified, the command search is performed using a default value of -.Ev PATH +.Va PATH that is guaranteed to find all of the standard utilities. .Pp If the @@ -1736,20 +1834,20 @@ .Ar editor string is a command name, subject to search via the -.Ev PATH +.Va PATH variable. The value in the -.Ev FCEDIT +.Va FCEDIT variable is used as a default when .Fl e is not specified. If -.Ev FCEDIT +.Va FCEDIT is null or unset, the value of the -.Ev EDITOR +.Va EDITOR variable is used. If -.Ev EDITOR +.Va EDITOR is null or unset, .Xr ed 1 is used as the editor. @@ -1783,7 +1881,7 @@ Select the commands to list or edit. The number of previous commands that can be accessed are determined by the value of the -.Ev HISTSIZE +.Va HISTSIZE variable. The value of .Ar first @@ -1814,12 +1912,12 @@ .El .El .Pp -The following environment variables affect the execution of +The following variables affect the execution of .Ic fc : -.Bl -tag -width ".Ev HISTSIZE" -.It Ev FCEDIT +.Bl -tag -width ".Va HISTSIZE" +.It Va FCEDIT Name of the editor to use for history editing. -.It Ev HISTSIZE +.It Va HISTSIZE The number of previous commands that are accessible. .El .It Ic fg Op Ar job @@ -2285,74 +2383,27 @@ .Sh ENVIRONMENT The following environment variables affect the execution of .Nm : -.Bl -tag -width ".Ev HISTSIZE" -.It Ev CDPATH -The search path used with the -.Ic cd -built-in. -.It Ev EDITOR -The fallback editor used with the -.Ic fc -built-in. -If not set, the default editor is -.Xr ed 1 . -.It Ev FCEDIT -The default editor used with the -.Ic fc -built-in. -.It Ev HISTSIZE -The number of previous commands that are accessible. -.It Ev HOME -The starting directory of -.Nm . -.It Ev IFS -Input Field Separators. -This is normally set to -.Aq space , -.Aq tab , -and -.Aq newline . -See the -.Sx White Space Splitting -section for more details. -.It Ev MAIL -The name of a mail file, that will be checked for the arrival of new -mail. -Overridden by -.Ev MAILPATH . -.It Ev MAILPATH -A colon -.Pq Ql \&: -separated list of file names, for the shell to check for incoming -mail. -This environment setting overrides the -.Ev MAIL -setting. -There is a maximum of 10 mailboxes that can be monitored at once. -.It Ev PATH -The default search path for executables. -See the -.Sx Path Search -section for details. -.It Ev PS1 -The primary prompt string, which defaults to -.Dq Li "$ " , -unless you are the superuser, in which case it defaults to -.Dq Li "# " . -.It Ev PS2 -The secondary prompt string, which defaults to -.Dq Li "> " . -.It Ev PS4 -The prefix for the trace output (if -.Fl x -is active). -The default is -.Dq Li "+ " . +.Bl -tag -width ".Ev LANGXXXXXX" +.It Ev ENV +Initialization file for interactive shells. +.It Ev LANG , Ev LC_* +Locale settings. +These are inherited by children of the shell, +and is used in a limited manner by the shell itself. +.It Ev PWD +An absolute pathname for the current directory, +possibly containing symbolic links. +This is used and updated by the shell. .It Ev TERM The default terminal setting for the shell. This is inherited by children of the shell, and is used in the history editing modes. .El +.Pp +Additionally, all environment variables are turned into shell variables +at startup, +which may affect the shell as described under +.Sx Special Variables . .Sh EXIT STATUS Errors that are detected by the shell, such as a syntax error, will cause the shell to exit with a non-zero exit status. Index: bin/sh/error.c =================================================================== --- bin/sh/error.c (revision 209146) +++ bin/sh/error.c (working copy) @@ -67,17 +67,21 @@ char *commandname; -static void exverror(int, const char *, va_list) __printf0like(2, 0); +static void exverror(int, const char *, va_list) __printf0like(2, 0) __dead2; /* * Called to raise an exception. Since C doesn't include exceptions, we * just do a longjmp to the exception handler. The type of exception is * stored in the global variable "exception". + * + * Interrupts are disabled; they should be reenabled when the exception is + * caught. */ void exraise(int e) { + INTOFF; if (handler == NULL) abort(); exception = e; @@ -138,8 +142,15 @@ static void exverror(int cond, const char *msg, va_list ap) { - CLEAR_PENDING_INT; - INTOFF; + /* + * An interrupt trumps an error. Certain places catch error + * exceptions or transform them to a plain nonzero exit code + * in child processes, and if an error exception can be handled, + * an interrupt can be handled as well. + * + * exraise() will disable interrupts for the exception handler. + */ + FORCEINTON; #ifdef DEBUG if (msg) @@ -149,8 +160,8 @@ #endif if (msg) { if (commandname) - outfmt(&errout, "%s: ", commandname); - doformat(&errout, msg, ap); + outfmt(out2, "%s: ", commandname); + doformat(out2, msg, ap); out2c('\n'); } flushall(); Index: bin/sh/error.h =================================================================== --- bin/sh/error.h (revision 209146) +++ bin/sh/error.h (working copy) @@ -72,14 +72,16 @@ #define INTOFF suppressint++ #define INTON { if (--suppressint == 0 && intpending) onint(); } +#define is_int_on() suppressint +#define SETINTON(s) suppressint = (s) #define FORCEINTON {suppressint = 0; if (intpending) onint();} #define CLEAR_PENDING_INT intpending = 0 #define int_pending() intpending -void exraise(int); +void exraise(int) __dead2; void onint(void); -void error(const char *, ...) __printf0like(1, 2); -void exerror(int, const char *, ...) __printf0like(2, 3); +void error(const char *, ...) __printf0like(1, 2) __dead2; +void exerror(int, const char *, ...) __printf0like(2, 3) __dead2; /* Index: bin/sh/input.c =================================================================== --- bin/sh/input.c (revision 209146) +++ bin/sh/input.c (working copy) @@ -93,7 +93,7 @@ int plinno = 1; /* input line number */ -MKINIT int parsenleft; /* copy of parsefile->nleft */ +int parsenleft; /* copy of parsefile->nleft */ MKINIT int parselleft; /* copy of parsefile->lleft */ char *parsenextc; /* copy of parsefile->nextc */ MKINIT struct parsefile basepf; /* top level input file */ @@ -111,9 +111,9 @@ INCLUDE "input.h" INCLUDE "error.h" -INIT { - extern char basebuf[]; +MKINIT char basebuf[]; +INIT { basepf.nextc = basepf.buf = basebuf; } @@ -215,7 +215,7 @@ if (flags >= 0 && flags & O_NONBLOCK) { flags &=~ O_NONBLOCK; if (fcntl(0, F_SETFL, flags) >= 0) { - out2str("sh: turning off NDELAY mode\n"); + out2fmt_flush("sh: turning off NDELAY mode\n"); goto retry; } } @@ -359,7 +359,7 @@ struct strpush *sp; INTOFF; -/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ +/*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/ if (parsefile->strpush) { sp = ckmalloc(sizeof (struct strpush)); sp->prev = parsefile->strpush; @@ -386,7 +386,7 @@ parsenextc = sp->prevstring; parsenleft = sp->prevnleft; parselleft = sp->prevlleft; -/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ +/*out2fmt_flush("*** calling popstring: restoring to '%s'\n", parsenextc);*/ if (sp->ap) sp->ap->flag &= ~ALIASINUSE; parsefile->strpush = sp->prev; @@ -401,7 +401,7 @@ */ void -setinputfile(char *fname, int push) +setinputfile(const char *fname, int push) { int fd; int fd2; @@ -509,6 +509,32 @@ /* + * Return current file (to go back to it later using popfilesupto()). + */ + +struct parsefile * +getcurrentfile(void) +{ + return parsefile; +} + + +/* + * Pop files until the given file is on top again. Useful for regular + * builtins that read shell commands from files or strings. + * If the given file is not an active file, an error is raised. + */ + +void +popfilesupto(struct parsefile *file) +{ + while (parsefile != file && parsefile != &basepf) + popfile(); + if (parsefile != file) + error("popfilesupto() misused"); +} + +/* * Return to top level. */ Index: bin/sh/input.h =================================================================== --- bin/sh/input.h (revision 209146) +++ bin/sh/input.h (working copy) @@ -45,6 +45,8 @@ extern char *parsenextc; /* next character in input buffer */ extern int init_editline; /* 0 == not setup, 1 == OK, -1 == failed */ +struct parsefile; + char *pfgets(char *, int); int pgetc(void); int preadbuffer(void); @@ -52,10 +54,12 @@ void pungetc(void); void pushstring(char *, int, void *); void popstring(void); -void setinputfile(char *, int); +void setinputfile(const char *, int); void setinputfd(int, int); void setinputstring(char *, int); void popfile(void); +struct parsefile *getcurrentfile(void); +void popfilesupto(struct parsefile *); void popallfiles(void); void closescript(void); Index: bin/sh/redir.c =================================================================== --- bin/sh/redir.c (revision 209146) +++ bin/sh/redir.c (working copy) @@ -63,6 +63,7 @@ #define EMPTY -2 /* marks an unused slot in redirtab */ +#define CLOSED -1 /* fd was not open before redir */ #define PIPESIZE 4096 /* amount of buffering in a pipe */ @@ -101,7 +102,6 @@ struct redirtab *sv = NULL; int i; int fd; - int try; char memory[10]; /* file descriptors to write to memory */ for (i = 10 ; --i >= 0 ; ) @@ -116,38 +116,30 @@ } for (n = redir ; n ; n = n->nfile.next) { fd = n->nfile.fd; - try = 0; if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && n->ndup.dupfd == fd) continue; /* redirect from/to same file descriptor */ if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { INTOFF; -again: if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { switch (errno) { case EBADF: - if (!try) { - openredirect(n, memory); - try++; - goto again; - } - /* FALLTHROUGH*/ + i = CLOSED; + break; default: INTON; error("%d: %s", fd, strerror(errno)); break; } - } - if (!try) { - sv->renamed[fd] = i; - } + } else + (void)fcntl(i, F_SETFD, FD_CLOEXEC); + sv->renamed[fd] = i; INTON; } if (fd == 0) fd0_redirected++; - if (!try) - openredirect(n, memory); + openredirect(n, memory); } if (memory[1]) out1 = &memout; @@ -166,8 +158,11 @@ /* * We suppress interrupts so that we won't leave open file - * descriptors around. This may not be such a good idea because - * an open of a device or a fifo can block indefinitely. + * descriptors around. Because the signal handler remains + * installed and we do not use system call restart, interrupts + * will still abort blocking opens such as fifos (they will fail + * with EINTR). There is, however, a race condition if an interrupt + * arrives after INTOFF and before open blocks. */ INTOFF; memory[fd] = 0; Index: bin/sh/trap.c =================================================================== --- bin/sh/trap.c (revision 209146) +++ bin/sh/trap.c (working copy) @@ -149,6 +149,7 @@ { char *action; int signo; + int errors = 0; if (argc <= 1) { for (signo = 0 ; signo < sys_nsig ; signo++) { @@ -183,8 +184,10 @@ } } while (*argv) { - if ((signo = sigstring_to_signum(*argv)) == -1) - error("bad signal %s", *argv); + if ((signo = sigstring_to_signum(*argv)) == -1) { + out2fmt_flush("trap: bad signal %s\n", *argv); + errors = 1; + } INTOFF; if (action) action = savestr(action); @@ -196,7 +199,7 @@ INTON; argv++; } - return 0; + return errors; } @@ -244,7 +247,8 @@ setsignal(int signo) { int action; - sig_t sig, sigact = SIG_DFL; + sig_t sigact = SIG_DFL; + struct sigaction sa; char *t; if ((t = trap[signo]) == NULL) @@ -320,9 +324,10 @@ case S_IGN: sigact = SIG_IGN; break; } *t = action; - sig = signal(signo, sigact); - if (sig != SIG_ERR && action == S_CATCH) - siginterrupt(signo, 1); + sa.sa_handler = sigact; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(signo, &sa, NULL); } Index: bin/sh/trap.h =================================================================== --- bin/sh/trap.h (revision 209146) +++ bin/sh/trap.h (working copy) @@ -45,4 +45,4 @@ void onsig(int); void dotrap(void); void setinteractive(int); -void exitshell(int); +void exitshell(int) __dead2; Index: bin/sh/arith.h =================================================================== --- bin/sh/arith.h (revision 209146) +++ bin/sh/arith.h (working copy) @@ -34,8 +34,8 @@ #define DIGITS(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3) -extern char *arith_buf, *arith_startbuf; +extern const char *arith_buf, *arith_startbuf; -arith_t arith(char *); +arith_t arith(const char *); void arith_lex_reset(void); int expcmd(int, char **); Index: bin/sh/mksyntax.c =================================================================== --- bin/sh/mksyntax.c (revision 209146) +++ bin/sh/mksyntax.c (working copy) @@ -55,8 +55,8 @@ struct synclass { - char *name; - char *comment; + const char *name; + const char *comment; }; /* Syntax classes */ @@ -101,16 +101,16 @@ static FILE *cfile; static FILE *hfile; -static char *syntax[513]; +static const char *syntax[513]; static int base; static int size; /* number of values which a char variable can have */ static int nbits; /* number of bits in a character */ static int digit_contig;/* true if digits are contiguous */ -static void filltable(char *); +static void filltable(const char *); static void init(void); -static void add(char *, char *); -static void print(char *); +static void add(const char *, const char *); +static void print(const char *); static void output_type_macros(void); static void digit_convert(void); @@ -232,7 +232,6 @@ add("\n", "CNL"); add("\\", "CBACK"); add("`", "CBQUOTE"); - add("'", "CSQUOTE"); add("\"", "CDQUOTE"); add("$", "CVAR"); add("}", "CENDVAR"); @@ -259,7 +258,7 @@ */ static void -filltable(char *dftval) +filltable(const char *dftval) { int i; @@ -293,7 +292,7 @@ */ static void -add(char *p, char *type) +add(const char *p, const char *type) { while (*p) syntax[*p++ + base] = type; @@ -306,7 +305,7 @@ */ static void -print(char *name) +print(const char *name) { int i; int col; @@ -338,7 +337,7 @@ * contiguous, we can test for them quickly. */ -static char *macro[] = { +static const char *macro[] = { "#define is_digit(c)\t((is_type+SYNBASE)[c] & ISDIGIT)", "#define is_eof(c)\t((c) == PEOF)", "#define is_alpha(c)\t(((c) < CTLESC || (c) > CTLQUOTEMARK) && isalpha((unsigned char) (c)))", @@ -351,7 +350,7 @@ static void output_type_macros(void) { - char **pp; + const char **pp; if (digit_contig) macro[0] = "#define is_digit(c)\t((unsigned int)((c) - '0') <= 9)"; Index: bin/sh/expand.c =================================================================== --- bin/sh/expand.c (revision 209146) +++ bin/sh/expand.c (working copy) @@ -109,7 +109,7 @@ STATIC void addfname(char *); STATIC struct strlist *expsort(struct strlist *); STATIC struct strlist *msort(struct strlist *, int); -STATIC int pmatch(char *, char *, int); +STATIC int pmatch(const char *, const char *, int); STATIC char *cvtnum(int, char *); STATIC int collate_range_cmp(int, int); @@ -273,8 +273,12 @@ while ((c = *p) != '\0') { switch(c) { - case CTLESC: - return (startp); + case CTLESC: /* This means CTL* are always considered quoted. */ + case CTLVAR: + case CTLBACKQ: + case CTLBACKQ | CTLQUOTE: + case CTLARI: + case CTLENDARI: case CTLQUOTEMARK: return (startp); case ':': @@ -282,6 +286,7 @@ goto done; break; case '/': + case CTLENDVAR: goto done; } p++; @@ -357,7 +362,7 @@ void expari(int flag) { - char *p, *start; + char *p, *q, *start; arith_t result; int begoff; int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); @@ -395,7 +400,9 @@ removerecordregions(begoff); if (quotes) rmescapes(p+2); + q = grabstackstr(expdest); result = arith(p+2); + ungrabstackstr(q, expdest); fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result); while (*p++) ; @@ -503,7 +510,9 @@ int amount; herefd = -1; - argstr(p, 0); + argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || + subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ? + EXP_CASE : 0) | EXP_TILDE); STACKSTRNUL(expdest); herefd = saveherefd; argbackq = saveargbackq; @@ -523,7 +532,7 @@ case VSQUESTION: if (*p != CTLENDVAR) { - outfmt(&errout, "%s\n", startp); + outfmt(out2, "%s\n", startp); error((char *)NULL); } error("%.*s: parameter %snot set", (int)(p - str - 1), @@ -659,7 +668,7 @@ } varlen = 0; startloc = expdest - stackblock(); - if (!set && uflag) { + if (!set && uflag && *var != '@' && *var != '*') { switch (subtype) { case VSNORMAL: case VSTRIMLEFT: @@ -852,7 +861,6 @@ int num; char *p; int i; - extern int oexitstatus; char sep; char **ap; char const *syntax; @@ -976,7 +984,7 @@ char *start; char *p; char *q; - char *ifs; + const char *ifs; const char *ifsspc; int had_param_ch = 0; @@ -1344,7 +1352,7 @@ */ int -patmatch(char *pattern, char *string, int squoted) +patmatch(const char *pattern, const char *string, int squoted) { #ifdef notdef if (pattern[0] == '!' && pattern[1] == '!') @@ -1356,9 +1364,9 @@ STATIC int -pmatch(char *pattern, char *string, int squoted) +pmatch(const char *pattern, const char *string, int squoted) { - char *p, *q; + const char *p, *q; char c; p = pattern; @@ -1406,7 +1414,7 @@ } while (*q++ != '\0'); return 0; case '[': { - char *endp; + const char *endp; int invert, found; char chr; @@ -1510,7 +1518,7 @@ */ int -casematch(union node *pattern, char *val) +casematch(union node *pattern, const char *val) { struct stackmark smark; int result; Index: bin/sh/mail.c =================================================================== --- bin/sh/mail.c (revision 209146) +++ bin/sh/mail.c (working copy) @@ -72,7 +72,7 @@ chkmail(int silent) { int i; - char *mpath; + const char *mpath; char *p; char *q; struct stackmark smark; Index: bin/sh/main.c =================================================================== --- bin/sh/main.c (revision 209146) +++ bin/sh/main.c (working copy) @@ -75,8 +75,9 @@ int rootpid; int rootshell; +struct jmploc main_handler; -STATIC void read_profile(char *); +STATIC void read_profile(const char *); STATIC char *find_dot_file(char *); /* @@ -90,14 +91,13 @@ int main(int argc, char *argv[]) { - struct jmploc jmploc; struct stackmark smark; volatile int state; char *shinit; (void) setlocale(LC_ALL, ""); state = 0; - if (setjmp(jmploc.loc)) { + if (setjmp(main_handler.loc)) { /* * When a shell procedure is executed, we raise the * exception EXSHELLPROC to clean up before executing @@ -143,7 +143,7 @@ else goto state4; } - handler = &jmploc; + handler = &main_handler; #ifdef DEBUG opentrace(); trputs("Shell args: "); trargs(argv); @@ -153,10 +153,7 @@ init(); setstackmark(&smark); procargs(argc, argv); - if (getpwd() == NULL && iflag) - out2str("sh: cannot determine working directory\n"); - if (getpwd() != NULL) - setvar ("PWD", getpwd(), VEXPORT); + pwd_init(iflag); if (iflag) chkmail(1); if (argv[0] && argv[0][0] == '-') { @@ -225,7 +222,7 @@ if (!stoppedjobs()) { if (!Iflag) break; - out2str("\nUse \"exit\" to leave shell.\n"); + out2fmt_flush("\nUse \"exit\" to leave shell.\n"); } numeof++; } else if (n != NULL && nflag == 0) { @@ -250,7 +247,7 @@ */ STATIC void -read_profile(char *name) +read_profile(const char *name) { int fd; @@ -271,7 +268,7 @@ */ void -readcmdfile(char *name) +readcmdfile(const char *name) { int fd; @@ -298,7 +295,7 @@ { static char localname[FILENAME_MAX+1]; char *fullname; - char *path = pathval(); + const char *path = pathval(); struct stat statb; /* don't try this for absolute or relative paths */ @@ -317,14 +314,20 @@ int dotcmd(int argc, char **argv) { - char *fullname; + char *filename, *fullname; if (argc < 2) error("missing filename"); exitstatus = 0; - fullname = find_dot_file(argv[1]); + /* + * Because we have historically not supported any options, + * only treat "--" specially. + */ + filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1]; + + fullname = find_dot_file(filename); setinputfile(fullname, 1); commandname = fullname; cmdloop(0); @@ -336,8 +339,6 @@ int exitcmd(int argc, char **argv) { - extern int oexitstatus; - if (stoppedjobs()) return 0; if (argc > 1) Index: bin/sh/expand.h =================================================================== --- bin/sh/expand.h (revision 209146) +++ bin/sh/expand.h (working copy) @@ -58,7 +58,7 @@ void expandhere(union node *, int); void expandarg(union node *, struct arglist *, int); void expari(int); -int patmatch(char *, char *, int); +int patmatch(const char *, const char *, int); void rmescapes(char *); -int casematch(union node *, char *); +int casematch(union node *, const char *); int wordexpcmd(int, char **); Index: bin/sh/exec.c =================================================================== --- bin/sh/exec.c (revision 209146) +++ bin/sh/exec.c (working copy) @@ -98,7 +98,7 @@ STATIC void tryexec(char *, char **, char **); STATIC void printentry(struct tblentry *, int); -STATIC struct tblentry *cmdlookup(char *, int); +STATIC struct tblentry *cmdlookup(const char *, int); STATIC void delete_cmd_entry(void); @@ -109,7 +109,7 @@ */ void -shellexec(char **argv, char **envp, char *path, int index) +shellexec(char **argv, char **envp, const char *path, int idx) { char *cmdname; int e; @@ -120,7 +120,7 @@ } else { e = ENOENT; while ((cmdname = padvance(&path, argv[0])) != NULL) { - if (--index < 0 && pathopt == NULL) { + if (--idx < 0 && pathopt == NULL) { tryexec(cmdname, argv, envp); if (errno != ENOENT && errno != ENOTDIR) e = errno; @@ -175,13 +175,13 @@ * NULL. */ -char *pathopt; +const char *pathopt; char * -padvance(char **path, char *name) +padvance(const char **path, const char *name) { - char *p, *q; - char *start; + const char *p, *start; + char *q; int len; if (*path == NULL) @@ -248,14 +248,14 @@ && (cmdp->cmdtype == CMDNORMAL || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) delete_cmd_entry(); - find_command(name, &entry, 1, pathval()); + find_command(name, &entry, DO_ERR, pathval()); if (verbose) { if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ cmdp = cmdlookup(name, 0); if (cmdp != NULL) printentry(cmdp, verbose); else - outfmt(&errout, "%s: not found\n", name); + outfmt(out2, "%s: not found\n", name); } flushall(); } @@ -268,17 +268,17 @@ STATIC void printentry(struct tblentry *cmdp, int verbose) { - int index; - char *path; + int idx; + const char *path; char *name; if (cmdp->cmdtype == CMDNORMAL) { - index = cmdp->param.index; + idx = cmdp->param.index; path = pathval(); do { name = padvance(&path, cmdp->cmdname); stunalloc(name); - } while (--index >= 0); + } while (--idx >= 0); out1str(name); } else if (cmdp->cmdtype == CMDBUILTIN) { out1fmt("builtin %s", cmdp->cmdname); @@ -310,10 +310,11 @@ */ void -find_command(char *name, struct cmdentry *entry, int printerr, char *path) +find_command(const char *name, struct cmdentry *entry, int act, + const char *path) { - struct tblentry *cmdp; - int index; + struct tblentry *cmdp, loc_cmd; + int idx; int prev; char *fullname; struct stat statb; @@ -329,13 +330,19 @@ } /* If name is in the table, and not invalidated by cd, we're done */ - if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) - goto success; + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) { + if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC) + cmdp = NULL; + else + goto success; + } /* If %builtin not in path, check for builtin next */ if (builtinloc < 0 && (i = find_builtin(name, &spec)) >= 0) { INTOFF; cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + cmdp = &loc_cmd; cmdp->cmdtype = CMDBUILTIN; cmdp->param.index = i; cmdp->special = spec; @@ -353,17 +360,19 @@ } e = ENOENT; - index = -1; + idx = -1; loop: while ((fullname = padvance(&path, name)) != NULL) { stunalloc(fullname); - index++; + idx++; if (pathopt) { if (prefix("builtin", pathopt)) { if ((i = find_builtin(name, &spec)) < 0) goto loop; INTOFF; cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + cmdp = &loc_cmd; cmdp->cmdtype = CMDBUILTIN; cmdp->param.index = i; cmdp->special = spec; @@ -376,8 +385,8 @@ } } /* if rehash, don't redo absolute path names */ - if (fullname[0] == '/' && index <= prev) { - if (index < prev) + if (fullname[0] == '/' && idx <= prev) { + if (idx < prev) goto loop; TRACE(("searchexec \"%s\": no change\n", name)); goto success; @@ -413,22 +422,25 @@ TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); INTOFF; cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + cmdp = &loc_cmd; cmdp->cmdtype = CMDNORMAL; - cmdp->param.index = index; + cmdp->param.index = idx; INTON; goto success; } /* We failed. If there was an entry for this command, delete it */ - if (cmdp) + if (cmdp && cmdp->cmdtype != CMDFUNCTION) delete_cmd_entry(); - if (printerr) { + if (act & DO_ERR) { if (e == ENOENT || e == ENOTDIR) outfmt(out2, "%s: not found\n", name); else outfmt(out2, "%s: %s\n", name, strerror(e)); } entry->cmdtype = CMDUNKNOWN; + entry->u.index = 0; return; success: @@ -445,7 +457,7 @@ */ int -find_builtin(char *name, int *special) +find_builtin(const char *name, int *special) { const struct builtincmd *bp; @@ -492,18 +504,18 @@ changepath(const char *newval) { const char *old, *new; - int index; + int idx; int firstchange; int bltin; old = pathval(); new = newval; firstchange = 9999; /* assume no change */ - index = 0; + idx = 0; bltin = -1; for (;;) { if (*old != *new) { - firstchange = index; + firstchange = idx; if ((*old == '\0' && *new == ':') || (*old == ':' && *new == '\0')) firstchange++; @@ -512,9 +524,9 @@ if (*new == '\0') break; if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) - bltin = index; + bltin = idx; if (*new == ':') { - index++; + idx++; } new++, old++; } @@ -607,10 +619,10 @@ STATIC struct tblentry * -cmdlookup(char *name, int add) +cmdlookup(const char *name, int add) { int hashval; - char *p; + const char *p; struct tblentry *cmdp; struct tblentry **pp; @@ -663,7 +675,7 @@ */ void -addcmdentry(char *name, struct cmdentry *entry) +addcmdentry(const char *name, struct cmdentry *entry) { struct tblentry *cmdp; @@ -683,7 +695,7 @@ */ void -defun(char *name, union node *func) +defun(const char *name, union node *func) { struct cmdentry entry; @@ -700,7 +712,7 @@ */ int -unsetfunc(char *name) +unsetfunc(const char *name) { struct tblentry *cmdp; @@ -718,19 +730,21 @@ */ int -typecmd_impl(int argc, char **argv, int cmd) +typecmd_impl(int argc, char **argv, int cmd, const char *path) { struct cmdentry entry; struct tblentry *cmdp; - char **pp; + const char *const *pp; struct alias *ap; int i; - int error = 0; - extern char *const parsekwd[]; + int error1 = 0; + + if (path != pathval()) + clearcmdentry(0); for (i = 1; i < argc; i++) { /* First look at the keywords */ - for (pp = (char **)parsekwd; *pp; pp++) + for (pp = parsekwd; *pp; pp++) if (**pp == *argv[i] && equal(*pp, argv[i])) break; @@ -760,16 +774,17 @@ } else { /* Finally use brute force */ - find_command(argv[i], &entry, 0, pathval()); + find_command(argv[i], &entry, 0, path); } switch (entry.cmdtype) { case CMDNORMAL: { if (strchr(argv[i], '/') == NULL) { - char *path = pathval(), *name; + const char *path2 = path; + char *name; int j = entry.u.index; do { - name = padvance(&path, argv[i]); + name = padvance(&path2, argv[i]); stunalloc(name); } while (--j >= 0); if (cmd == TYPECMD_SMALLV) @@ -790,7 +805,7 @@ if (cmd != TYPECMD_SMALLV) outfmt(out2, "%s: %s\n", argv[i], strerror(errno)); - error |= 127; + error1 |= 127; } } break; @@ -815,11 +830,15 @@ default: if (cmd != TYPECMD_SMALLV) outfmt(out2, "%s: not found\n", argv[i]); - error |= 127; + error1 |= 127; break; } } - return error; + + if (path != pathval()) + clearcmdentry(0); + + return error1; } /* @@ -829,5 +848,5 @@ int typecmd(int argc, char **argv) { - return typecmd_impl(argc, argv, TYPECMD_TYPE); + return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1)); } Index: bin/sh/miscbltin.c =================================================================== --- bin/sh/miscbltin.c (revision 209146) +++ bin/sh/miscbltin.c (working copy) @@ -93,7 +93,7 @@ char c; int rflag; char *prompt; - char *ifs; + const char *ifs; char *p; int startword; int status; @@ -254,7 +254,7 @@ int -umaskcmd(int argc __unused, char **argv) +umaskcmd(int argc __unused, char **argv __unused) { char *ap; int mask; Index: bin/sh/cd.c =================================================================== --- bin/sh/cd.c (revision 209146) +++ bin/sh/cd.c (working copy) @@ -70,6 +70,7 @@ STATIC char *getcomponent(void); STATIC char *findcwd(char *); STATIC void updatepwd(char *); +STATIC char *getpwd(void); STATIC char *getpwd2(void); STATIC char *curdir = NULL; /* current working directory */ @@ -79,8 +80,8 @@ int cdcmd(int argc, char **argv) { - char *dest; - char *path; + const char *dest; + const char *path; char *p; struct stat statb; int ch, phys, print = 0; @@ -351,7 +352,7 @@ /* * Get the current directory and cache the result in curdir. */ -char * +STATIC char * getpwd(void) { char *p; @@ -374,7 +375,6 @@ STATIC char * getpwd2(void) { - struct stat stdot, stpwd; char *pwd; int i; @@ -387,12 +387,29 @@ break; } - pwd = getenv("PWD"); + return NULL; +} + +/* + * Initialize PWD in a new shell. + * If the shell is interactive, we need to warn if this fails. + */ +void +pwd_init(int warn) +{ + char *pwd; + struct stat stdot, stpwd; + + pwd = lookupvar("PWD"); if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && stat(pwd, &stpwd) != -1 && stdot.st_dev == stpwd.st_dev && stdot.st_ino == stpwd.st_ino) { - return pwd; + if (curdir) + ckfree(curdir); + curdir = savestr(pwd); } - return NULL; + if (getpwd() == NULL && warn) + out2fmt_flush("sh: cannot determine working directory\n"); + setvar("PWD", curdir, VEXPORT); } Index: bin/sh/eval.c =================================================================== --- bin/sh/eval.c (revision 209146) +++ bin/sh/eval.c (working copy) @@ -74,7 +74,7 @@ #endif -MKINIT int evalskip; /* set if we are skipping commands */ +int evalskip; /* set if we are skipping commands */ STATIC int skipcount; /* number of levels to skip */ MKINIT int loopnest; /* current loop nesting level */ int funcnest; /* depth of function calls */ @@ -91,6 +91,7 @@ STATIC void evalfor(union node *, int); STATIC void evalcase(union node *, int); STATIC void evalsubshell(union node *, int); +STATIC void evalredir(union node *, int); STATIC void expredir(union node *); STATIC void evalpipe(union node *); STATIC void evalcommand(union node *, int, struct backcmd *); @@ -221,10 +222,7 @@ evaltree(n->nbinary.ch2, flags); break; case NREDIR: - expredir(n->nredir.redirect); - redirect(n->nredir.redirect, REDIR_PUSH); - evaltree(n->nredir.n, flags); - popredir(); + evalredir(n, flags); break; case NSUBSHELL: evalsubshell(n, flags); @@ -407,8 +405,7 @@ flags &=~ EV_TESTED; redirect(n->nredir.redirect, 0); evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ - } - if (! backgnd) { + } else if (! backgnd) { INTOFF; exitstatus = waitforjob(jp, (int *)NULL); INTON; @@ -416,6 +413,46 @@ } +/* + * Evaluate a redirected compound command. + */ + +STATIC void +evalredir(union node *n, int flags) +{ + struct jmploc jmploc; + struct jmploc *savehandler; + volatile int in_redirect = 1; + + expredir(n->nredir.redirect); + savehandler = handler; + if (setjmp(jmploc.loc)) { + int e; + + handler = savehandler; + e = exception; + if (e == EXERROR || e == EXEXEC) { + popredir(); + if (in_redirect) { + exitstatus = 2; + return; + } + } + longjmp(handler->loc, 1); + } else { + INTOFF; + handler = &jmploc; + redirect(n->nredir.redirect, REDIR_PUSH); + in_redirect = 0; + INTON; + evaltree(n->nredir.n, flags); + } + INTOFF; + handler = savehandler; + popredir(); + INTON; +} + /* * Compute the names of the files in a redirection list. @@ -593,10 +630,12 @@ char *savecmdname; struct shparam saveparam; struct localvar *savelocalvars; + struct parsefile *savetopfile; volatile int e; char *lastarg; int realstatus; int do_clearcmdentry; + char *path = pathval(); /* First expand the arguments. */ TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); @@ -646,7 +685,7 @@ out2str(ps4val()); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) - outc(' ', &errout); + out2c(' '); p = sp->text; while (*p != '=' && *p != '\0') out2c(*p++); @@ -658,7 +697,7 @@ } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) - outc(' ', &errout); + out2c(' '); /* Disambiguate command looking like assignment. */ if (sp == arglist.list && strchr(sp->text, '=') != NULL && @@ -670,7 +709,7 @@ out2qstr(sp->text); sep = ' '; } - outc('\n', &errout); + out2c('\n'); flushout(&errout); } @@ -679,10 +718,10 @@ /* Variable assignment(s) without command */ cmdentry.cmdtype = CMDBUILTIN; cmdentry.u.index = BLTINCMD; - cmdentry.special = 1; + cmdentry.special = 0; } else { static const char PATH[] = "PATH="; - char *path = pathval(); + int cmd_flags = 0, bltinonly = 0; /* * Modify the command lookup path, if a PATH= assignment @@ -713,42 +752,79 @@ do_clearcmdentry = 1; } - find_command(argv[0], &cmdentry, 1, path); - if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ - exitstatus = 127; - flushout(&errout); - return; - } - /* implement the bltin builtin here */ - if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { - for (;;) { - argv++; - if (--argc == 0) + for (;;) { + if (bltinonly) { + cmdentry.u.index = find_builtin(*argv, &cmdentry.special); + if (cmdentry.u.index < 0) { + cmdentry.u.index = BLTINCMD; + argv--; + argc++; break; - if ((cmdentry.u.index = find_builtin(*argv, - &cmdentry.special)) < 0) { - outfmt(&errout, "%s: not found\n", *argv); - exitstatus = 127; - flushout(&errout); - return; } - if (cmdentry.u.index != BLTINCMD) + } else + find_command(argv[0], &cmdentry, cmd_flags, path); + /* implement the bltin and command builtins here */ + if (cmdentry.cmdtype != CMDBUILTIN) + break; + if (cmdentry.u.index == BLTINCMD) { + if (argc == 1) break; - } + argv++; + argc--; + bltinonly = 1; + } else if (cmdentry.u.index == COMMANDCMD) { + if (argc == 1) + break; + if (!strcmp(argv[1], "-p")) { + if (argc == 2) + break; + if (argv[2][0] == '-') { + if (strcmp(argv[2], "--")) + break; + if (argc == 3) + break; + argv += 3; + argc -= 3; + } else { + argv += 2; + argc -= 2; + } + path = _PATH_STDPATH; + clearcmdentry(0); + do_clearcmdentry = 1; + } else if (!strcmp(argv[1], "--")) { + if (argc == 2) + break; + argv += 2; + argc -= 2; + } else if (argv[1][0] == '-') + break; + else { + argv++; + argc--; + } + cmd_flags |= DO_NOFUNC; + bltinonly = 0; + } else + break; } + /* + * Special builtins lose their special properties when + * called via 'command'. + */ + if (cmd_flags & DO_NOFUNC) + cmdentry.special = 0; } /* Fork off a child process if necessary. */ if (cmd->ncmd.backgnd - || (cmdentry.cmdtype == CMDNORMAL + || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) && ((flags & EV_EXIT) == 0 || have_traps())) || ((flags & EV_BACKCMD) != 0 && (cmdentry.cmdtype != CMDBUILTIN || cmdentry.u.index == CDCMD || cmdentry.u.index == DOTCMD - || cmdentry.u.index == EVALCMD)) - || (cmdentry.cmdtype == CMDBUILTIN && - cmdentry.u.index == COMMANDCMD)) { + || cmdentry.u.index == EVALCMD))) { jp = makejob(cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { @@ -775,7 +851,6 @@ #ifdef DEBUG trputs("Shell function: "); trargs(argv); #endif - redirect(cmd->ncmd.redirect, REDIR_PUSH); saveparam = shellparam; shellparam.malloc = 0; shellparam.reset = 1; @@ -786,7 +861,6 @@ savelocalvars = localvars; localvars = NULL; reffunc(cmdentry.u.func); - INTON; savehandler = handler; if (setjmp(jmploc.loc)) { if (exception == EXSHELLPROC) @@ -794,23 +868,27 @@ else { freeparam(&shellparam); shellparam = saveparam; + if (exception == EXERROR || exception == EXEXEC) + popredir(); } unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; + funcnest--; handler = savehandler; longjmp(handler->loc, 1); } handler = &jmploc; + funcnest++; + redirect(cmd->ncmd.redirect, REDIR_PUSH); + INTON; for (sp = varlist.list ; sp ; sp = sp->next) mklocal(sp->text); - funcnest++; exitstatus = oexitstatus; if (flags & EV_TESTED) evaltree(getfuncnode(cmdentry.u.func), EV_TESTED); else evaltree(getfuncnode(cmdentry.u.func), 0); - funcnest--; INTOFF; unreffunc(cmdentry.u.func); poplocalvars(); @@ -818,6 +896,7 @@ freeparam(&shellparam); shellparam = saveparam; handler = savehandler; + funcnest--; popredir(); INTON; if (evalskip == SKIPFUNC) { @@ -836,8 +915,10 @@ memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; + cmdentry.special = 0; } savecmdname = commandname; + savetopfile = getcurrentfile(); cmdenviron = varlist.list; e = -1; savehandler = handler; @@ -848,15 +929,25 @@ } handler = &jmploc; redirect(cmd->ncmd.redirect, mode); + /* + * If there is no command word, redirection errors should + * not be fatal but assignment errors should. + */ + if (argc == 0 && !(flags & EV_BACKCMD)) + cmdentry.special = 1; if (cmdentry.special) listsetvar(cmdenviron); + if (argc > 0) + bltinsetlocale(); commandname = argv[0]; argptr = argv + 1; - optptr = NULL; /* initialize nextopt */ + nextopt_optptr = NULL; /* initialize nextopt */ builtin_flags = flags; exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); flushall(); cmddone: + if (argc > 0) + bltinunsetlocale(); cmdenviron = NULL; out1 = &output; out2 = &errout; @@ -868,29 +959,31 @@ } } handler = savehandler; - if (e != -1) { - if ((e != EXERROR && e != EXEXEC) - || cmdentry.special) - exraise(e); - FORCEINTON; - } - if (cmdentry.u.index != EXECCMD) - popredir(); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; memout.buf = NULL; } + if (cmdentry.u.index != EXECCMD && + (e == -1 || e == EXERROR || e == EXEXEC)) + popredir(); + if (e != -1) { + if ((e != EXERROR && e != EXEXEC) + || cmdentry.special) + exraise(e); + popfilesupto(savetopfile); + if (flags != EV_BACKCMD) + FORCEINTON; + } } else { #ifdef DEBUG trputs("normal command: "); trargs(argv); #endif - clearredir(); redirect(cmd->ncmd.redirect, 0); for (sp = varlist.list ; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); - shellexec(argv, envp, pathval(), cmdentry.u.index); + shellexec(argv, envp, path, cmdentry.u.index); /*NOTREACHED*/ } goto out; @@ -946,12 +1039,17 @@ */ /* - * No command given, or a bltin command with no arguments. + * No command given, a bltin command with no arguments, or a bltin command + * with an invalid name. */ int -bltincmd(int argc __unused, char **argv __unused) +bltincmd(int argc, char **argv) { + if (argc > 1) { + out2fmt_flush("%s: not found\n", argv[1]); + return 127; + } /* * Preserve exitstatus of a previous possible redirection * as POSIX mandates @@ -991,23 +1089,18 @@ int commandcmd(int argc, char **argv) { - static char stdpath[] = _PATH_STDPATH; - struct jmploc loc, *old; - struct strlist *sp; - char *path; + const char *path; int ch; int cmd = -1; - for (sp = cmdenviron; sp ; sp = sp->next) - setvareq(sp->text, VEXPORT|VSTACK); - path = pathval(); + path = bltinlookup("PATH", 1); optind = optreset = 1; opterr = 0; while ((ch = getopt(argc, argv, "pvV")) != -1) { switch (ch) { case 'p': - path = stdpath; + path = _PATH_STDPATH; break; case 'v': cmd = TYPECMD_SMALLV; @@ -1026,24 +1119,16 @@ if (cmd != -1) { if (argc != 1) error("wrong number of arguments"); - return typecmd_impl(2, argv - 1, cmd); - } - if (argc != 0) { - old = handler; - handler = &loc; - if (setjmp(handler->loc) == 0) - shellexec(argv, environment(), path, 0); - handler = old; - if (exception == EXEXEC) - exit(exerrno); - exraise(exception); + return typecmd_impl(2, argv - 1, cmd, path); } + if (argc != 0) + error("commandcmd() called while it should not be"); /* * Do nothing successfully if no command was specified; * ksh also does this. */ - exit(0); + return 0; } @@ -1085,6 +1170,12 @@ int execcmd(int argc, char **argv) { + /* + * Because we have historically not supported any options, + * only treat "--" specially. + */ + if (argc > 1 && strcmp(argv[1], "--") == 0) + argc--, argv++; if (argc > 1) { struct strlist *sp; Index: bin/sh/var.c =================================================================== --- bin/sh/var.c (revision 209146) +++ bin/sh/var.c (working copy) @@ -73,13 +73,14 @@ struct varinit { struct var *var; int flags; - char *text; + const char *text; void (*func)(const char *); }; #ifndef NO_HISTORY struct var vhistsize; +struct var vterm; #endif struct var vifs; struct var vmail; @@ -94,27 +95,31 @@ STATIC const struct varinit varinit[] = { #ifndef NO_HISTORY - { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", + { &vhistsize, VUNSET, "HISTSIZE=", sethistsize }, #endif - { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", + { &vifs, 0, "IFS= \t\n", NULL }, - { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", + { &vmail, VUNSET, "MAIL=", NULL }, - { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", + { &vmpath, VUNSET, "MAILPATH=", NULL }, - { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH, + { &vpath, 0, "PATH=" _PATH_DEFPATH, changepath }, - { &vppid, VSTRFIXED|VTEXTFIXED|VUNSET, "PPID=", + { &vppid, VUNSET, "PPID=", NULL }, /* * vps1 depends on uid */ - { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", + { &vps2, 0, "PS2=> ", NULL }, - { &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ", + { &vps4, 0, "PS4=+ ", NULL }, - { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", +#ifndef NO_HISTORY + { &vterm, VUNSET, "TERM=", + setterm }, +#endif + { &voptind, 0, "OPTIND=1", getoptsreset }, { NULL, 0, NULL, NULL } @@ -122,9 +127,17 @@ STATIC struct var *vartab[VTABSIZE]; -STATIC struct var **hashvar(char *); -STATIC int varequal(char *, char *); -STATIC int localevar(char *); +STATIC const char *const locale_names[7] = { + "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", + "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL +}; +STATIC const int locale_categories[7] = { + LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0 +}; + +STATIC struct var **hashvar(const char *); +STATIC int varequal(const char *, const char *); +STATIC int localevar(const char *); /* * Initialize the variable symbol tables and import the environment. @@ -132,9 +145,9 @@ #ifdef mkinit INCLUDE "var.h" +MKINIT char **environ; INIT { char **envp; - extern char **environ; initvar(); for (envp = environ ; *envp ; envp++) { @@ -164,8 +177,8 @@ vpp = hashvar(ip->text); vp->next = *vpp; *vpp = vp; - vp->text = ip->text; - vp->flags = ip->flags; + vp->text = __DECONST(char *, ip->text); + vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED; vp->func = ip->func; } } @@ -176,7 +189,7 @@ vpp = hashvar("PS1="); vps1.next = *vpp; *vpp = &vps1; - vps1.text = geteuid() ? "PS1=$ " : "PS1=# "; + vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# "); vps1.flags = VSTRFIXED|VTEXTFIXED; } if ((vppid.flags & VEXPORT) == 0) { @@ -190,12 +203,14 @@ */ int -setvarsafe(char *name, char *val, int flags) +setvarsafe(const char *name, const char *val, int flags) { struct jmploc jmploc; struct jmploc *const savehandler = handler; int err = 0; + int inton; + inton = is_int_on(); if (setjmp(jmploc.loc)) err = 1; else { @@ -203,6 +218,7 @@ setvar(name, val, flags); } handler = savehandler; + SETINTON(inton); return err; } @@ -212,9 +228,9 @@ */ void -setvar(char *name, char *val, int flags) +setvar(const char *name, const char *val, int flags) { - char *p, *q; + const char *p; int len; int namelen; char *nameeq; @@ -242,25 +258,20 @@ } else { len += strlen(val); } - p = nameeq = ckmalloc(len); - q = name; - while (--namelen >= 0) - *p++ = *q++; - *p++ = '='; - *p = '\0'; + nameeq = ckmalloc(len); + memcpy(nameeq, name, namelen); + nameeq[namelen] = '='; if (val) - scopy(val, p); + scopy(val, nameeq + namelen + 1); + else + nameeq[namelen + 1] = '\0'; setvareq(nameeq, flags); } STATIC int -localevar(char *s) +localevar(const char *s) { - static char *lnames[7] = { - "ALL", "COLLATE", "CTYPE", "MONETARY", - "NUMERIC", "TIME", NULL - }; - char **ss; + const char *const *ss; if (*s != 'L') return 0; @@ -268,8 +279,10 @@ return 1; if (strncmp(s + 1, "C_", 2) != 0) return 0; - for (ss = lnames; *ss ; ss++) - if (varequal(s + 3, *ss)) + if (varequal(s + 3, "ALL")) + return 1; + for (ss = locale_names; *ss ; ss++) + if (varequal(s + 3, *ss + 3)) return 1; return 0; } @@ -280,7 +293,7 @@ * pointer into environ where the string should not be manipulated. */ static void -change_env(char *s, int set) +change_env(const char *s, int set) { char *eqp; char *ss; @@ -391,7 +404,7 @@ */ char * -lookupvar(char *name) +lookupvar(const char *name) { struct var *v; @@ -414,7 +427,7 @@ */ char * -bltinlookup(char *name, int doall) +bltinlookup(const char *name, int doall) { struct strlist *sp; struct var *v; @@ -435,6 +448,61 @@ } +/* + * Set up locale for a builtin (LANG/LC_* assignments). + */ +void +bltinsetlocale(void) +{ + struct strlist *lp; + int act = 0; + char *loc, *locdef; + int i; + + for (lp = cmdenviron ; lp ; lp = lp->next) { + if (localevar(lp->text)) { + act = 1; + break; + } + } + if (!act) + return; + loc = bltinlookup("LC_ALL", 0); + INTOFF; + if (loc != NULL) { + setlocale(LC_ALL, loc); + INTON; + return; + } + locdef = bltinlookup("LANG", 0); + for (i = 0; locale_names[i] != NULL; i++) { + loc = bltinlookup(locale_names[i], 0); + if (loc == NULL) + loc = locdef; + if (loc != NULL) + setlocale(locale_categories[i], loc); + } + INTON; +} + +/* + * Undo the effect of bltinlocaleset(). + */ +void +bltinunsetlocale(void) +{ + struct strlist *lp; + + INTOFF; + for (lp = cmdenviron ; lp ; lp = lp->next) { + if (localevar(lp->text)) { + setlocale(LC_ALL, ""); + return; + } + } + INTON; +} + /* * Generate a list of exported variables. This routine is used to construct @@ -472,9 +540,9 @@ * VSTACK set since these are currently allocated on the stack. */ -#ifdef mkinit MKINIT void shprocvar(void); +#ifdef mkinit SHELLPROC { shprocvar(); } @@ -798,7 +866,7 @@ */ int -unsetvar(char *s) +unsetvar(const char *s) { struct var **vpp; struct var *vp; @@ -838,7 +906,7 @@ */ STATIC struct var ** -hashvar(char *p) +hashvar(const char *p) { unsigned int hashval; @@ -857,7 +925,7 @@ */ STATIC int -varequal(char *p, char *q) +varequal(const char *p, const char *q) { while (*p == *q++) { if (*p++ == '=') Index: bin/sh/memalloc.c =================================================================== --- bin/sh/memalloc.c (revision 209146) +++ bin/sh/memalloc.c (working copy) @@ -95,7 +95,7 @@ */ char * -savestr(char *s) +savestr(const char *s) { char *p; Index: bin/sh/main.h =================================================================== --- bin/sh/main.h (revision 209146) +++ bin/sh/main.h (working copy) @@ -35,8 +35,9 @@ extern int rootpid; /* pid of main shell */ extern int rootshell; /* true if we aren't a child of the main shell */ +extern struct jmploc main_handler; /* top level exception handler */ -void readcmdfile(char *); +void readcmdfile(const char *); void cmdloop(int); int dotcmd(int, char **); int exitcmd(int, char **); Index: bin/sh/exec.h =================================================================== --- bin/sh/exec.h (revision 209146) +++ bin/sh/exec.h (working copy) @@ -57,20 +57,24 @@ }; -extern char *pathopt; /* set by padvance */ +/* action to find_command() */ +#define DO_ERR 0x01 /* prints errors */ +#define DO_NOFUNC 0x02 /* don't return shell functions, for command */ + +extern const char *pathopt; /* set by padvance */ extern int exerrno; /* last exec error */ -void shellexec(char **, char **, char *, int); -char *padvance(char **, char *); +void shellexec(char **, char **, const char *, int) __dead2; +char *padvance(const char **, const char *); int hashcmd(int, char **); -void find_command(char *, struct cmdentry *, int, char *); -int find_builtin(char *, int *); +void find_command(const char *, struct cmdentry *, int, const char *); +int find_builtin(const char *, int *); void hashcd(void); void changepath(const char *); void deletefuncs(void); -void addcmdentry(char *, struct cmdentry *); -void defun(char *, union node *); -int unsetfunc(char *); -int typecmd_impl(int, char **, int); +void addcmdentry(const char *, struct cmdentry *); +void defun(const char *, union node *); +int unsetfunc(const char *); +int typecmd_impl(int, char **, int, const char *); int typecmd(int, char **); void clearcmdentry(int); Index: bin/sh/alias.c =================================================================== --- bin/sh/alias.c (revision 209146) +++ bin/sh/alias.c (working copy) @@ -52,13 +52,13 @@ STATIC struct alias *atab[ATABSIZE]; STATIC int aliases; -STATIC void setalias(char *, char *); +STATIC void setalias(const char *, const char *); STATIC int unalias(const char *); STATIC struct alias **hashalias(const char *); STATIC void -setalias(char *name, char *val) +setalias(const char *name, const char *val) { struct alias *ap, **app; @@ -176,7 +176,7 @@ } struct alias * -lookupalias(char *name, int check) +lookupalias(const char *name, int check) { struct alias *ap = *hashalias(name); Index: bin/sh/cd.h =================================================================== --- bin/sh/cd.h (revision 209146) +++ bin/sh/cd.h (working copy) @@ -29,6 +29,6 @@ * $FreeBSD$ */ -char *getpwd(void); +void pwd_init(int); int cdcmd (int, char **); int pwdcmd(int, char **); Index: bin/sh/mkinit.c =================================================================== --- bin/sh/mkinit.c (revision 209146) +++ bin/sh/mkinit.c (working copy) @@ -102,9 +102,9 @@ */ struct event { - char *name; /* name of event (e.g. INIT) */ - char *routine; /* name of routine called on event */ - char *comment; /* comment describing routine */ + const char *name; /* name of event (e.g. INIT) */ + const char *routine; /* name of routine called on event */ + const char *comment; /* comment describing routine */ struct text code; /* code for handling event */ }; @@ -140,7 +140,7 @@ }; -char *curfile; /* current file */ +const char *curfile; /* current file */ int linno; /* current line */ char *header_files[200]; /* list of header files */ struct text defines; /* #define statements */ @@ -148,20 +148,20 @@ int amiddecls; /* for formatting */ -void readfile(char *); -int match(char *, char *); -int gooddefine(char *); -void doevent(struct event *, FILE *, char *); +void readfile(const char *); +int match(const char *, const char *); +int gooddefine(const char *); +void doevent(struct event *, FILE *, const char *); void doinclude(char *); void dodecl(char *, FILE *); void output(void); -void addstr(char *, struct text *); +void addstr(const char *, struct text *); void addchar(int, struct text *); void writetext(struct text *, FILE *); -FILE *ckfopen(char *, char *); +FILE *ckfopen(const char *, const char *); void *ckmalloc(size_t); -char *savestr(char *); -void error(char *); +char *savestr(const char *); +void error(const char *); #define equal(s1, s2) (strcmp(s1, s2) == 0) @@ -170,9 +170,9 @@ { char **ap; - header_files[0] = "\"shell.h\""; - header_files[1] = "\"mystring.h\""; - header_files[2] = "\"init.h\""; + header_files[0] = savestr("\"shell.h\""); + header_files[1] = savestr("\"mystring.h\""); + header_files[2] = savestr("\"init.h\""); for (ap = argv + 1 ; *ap ; ap++) readfile(*ap); output(); @@ -186,7 +186,7 @@ */ void -readfile(char *fname) +readfile(const char *fname) { FILE *fp; char line[1024]; @@ -230,9 +230,9 @@ int -match(char *name, char *line) +match(const char *name, const char *line) { - char *p, *q; + const char *p, *q; p = name, q = line; while (*p) { @@ -246,9 +246,9 @@ int -gooddefine(char *line) +gooddefine(const char *line) { - char *p; + const char *p; if (! match("#define", line)) return 0; /* not a define */ @@ -269,11 +269,11 @@ void -doevent(struct event *ep, FILE *fp, char *fname) +doevent(struct event *ep, FILE *fp, const char *fname) { char line[1024]; int indent; - char *p; + const char *p; sprintf(line, "\n /* from %s: */\n", fname); addstr(line, &ep->code); @@ -407,7 +407,7 @@ */ void -addstr(char *s, struct text *text) +addstr(const char *s, struct text *text) { while (*s) { if (--text->nleft < 0) @@ -452,7 +452,7 @@ } FILE * -ckfopen(char *file, char *mode) +ckfopen(const char *file, const char *mode) { FILE *fp; @@ -474,7 +474,7 @@ } char * -savestr(char *s) +savestr(const char *s) { char *p; @@ -484,7 +484,7 @@ } void -error(char *msg) +error(const char *msg) { if (curfile != NULL) fprintf(stderr, "%s:%d: ", curfile, linno); Index: bin/sh/options.c =================================================================== --- bin/sh/options.c (revision 209146) +++ bin/sh/options.c (working copy) @@ -64,7 +64,7 @@ struct shparam shellparam; /* current positional parameters */ char **argptr; /* argument list for builtin commands */ char *shoptarg; /* set by nextopt (like getopt) */ -char *optptr; /* used by nextopt */ +char *nextopt_optptr; /* used by nextopt */ char *minusc; /* argument to -c option */ @@ -93,8 +93,11 @@ options(1); if (*argptr == NULL && minusc == NULL) sflag = 1; - if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) + if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) { iflag = 1; + if (Eflag == 2) + Eflag = 1; + } if (mflag == 2) mflag = iflag; for (i = 0; i < NOPTS; i++) @@ -554,12 +557,13 @@ */ int -nextopt(char *optstring) +nextopt(const char *optstring) { - char *p, *q; + char *p; + const char *q; char c; - if ((p = optptr) == NULL || *p == '\0') { + if ((p = nextopt_optptr) == NULL || *p == '\0') { p = *argptr; if (p == NULL || *p != '-' || *++p == '\0') return '\0'; @@ -580,6 +584,6 @@ shoptarg = p; p = NULL; } - optptr = p; + nextopt_optptr = p; return c; } Index: bin/sh/eval.h =================================================================== --- bin/sh/eval.h (revision 209146) +++ bin/sh/eval.h (working copy) @@ -35,6 +35,7 @@ extern char *commandname; /* currently executing command */ extern int exitstatus; /* exit status of last command */ +extern int oexitstatus; /* saved exit status */ extern struct strlist *cmdenviron; /* environment for builtin command */ Index: bin/sh/parser.c =================================================================== --- bin/sh/parser.c (revision 209146) +++ bin/sh/parser.c (working copy) @@ -79,10 +79,13 @@ int striptabs; /* if set, strip leading tabs */ }; +struct parser_temp { + struct parser_temp *next; + void *data; +}; STATIC struct heredoc *heredoclist; /* list of here documents to read */ -STATIC int parsebackquote; /* nonzero if we are inside backquotes */ STATIC int doprompt; /* if set, prompt the user */ STATIC int needprompt; /* true if interactive and at start of line */ STATIC int lasttoken; /* last token read */ @@ -95,6 +98,7 @@ STATIC int quoteflag; /* set if (part of) last token was quoted */ STATIC int startlinno; /* line # where last token started */ STATIC int funclinno; /* line # where the current function started */ +STATIC struct parser_temp *parser_temp; /* XXX When 'noaliases' is set to one, no alias expansion takes place. */ static int noaliases = 0; @@ -114,10 +118,77 @@ STATIC int readtoken1(int, char const *, char *, int); STATIC int noexpand(char *); STATIC void synexpect(int); -STATIC void synerror(char *); +STATIC void synerror(const char *); STATIC void setprompt(int); +STATIC void * +parser_temp_alloc(size_t len) +{ + struct parser_temp *t; + + INTOFF; + t = ckmalloc(sizeof(*t)); + t->data = NULL; + t->next = parser_temp; + parser_temp = t; + t->data = ckmalloc(len); + INTON; + return t->data; +} + + +STATIC void * +parser_temp_realloc(void *ptr, size_t len) +{ + struct parser_temp *t; + + INTOFF; + t = parser_temp; + if (ptr != t->data) + error("bug: parser_temp_realloc misused"); + t->data = ckrealloc(t->data, len); + INTON; + return t->data; +} + + +STATIC void +parser_temp_free_upto(void *ptr) +{ + struct parser_temp *t; + int done = 0; + + INTOFF; + while (parser_temp != NULL && !done) { + t = parser_temp; + parser_temp = t->next; + done = t->data == ptr; + ckfree(t->data); + ckfree(t); + } + INTON; + if (!done) + error("bug: parser_temp_free_upto misused"); +} + + +STATIC void +parser_temp_free_all(void) +{ + struct parser_temp *t; + + INTOFF; + while (parser_temp != NULL) { + t = parser_temp; + parser_temp = t->next; + ckfree(t->data); + ckfree(t); + } + INTON; +} + + /* * Read and parse a command. Returns NEOF on end of file. (NULL is a * valid parse tree indicating a blank line.) @@ -128,6 +199,12 @@ { int t; + /* This assumes the parser is not re-entered, + * which could happen if we add command substitution on PS1/PS2. + */ + parser_temp_free_all(); + heredoclist = NULL; + tokpushback = 0; doprompt = interact; if (doprompt) @@ -864,6 +941,188 @@ } +#define MAXNEST_STATIC 8 +struct tokenstate +{ + const char *syntax; /* *SYNTAX */ + int parenlevel; /* levels of parentheses in arithmetic */ + enum tokenstate_category + { + TSTATE_TOP, + TSTATE_VAR_OLD, /* ${var+-=?}, inherits dquotes */ + TSTATE_VAR_NEW, /* other ${var...}, own dquote state */ + TSTATE_ARITH + } category; +}; + + +/* + * Called to parse command substitutions. + */ + +STATIC char * +parsebackq(char *out, struct nodelist **pbqlist, + int oldstyle, int dblquote, int quoted) +{ + struct nodelist **nlpp; + union node *n; + char *volatile str; + struct jmploc jmploc; + struct jmploc *const savehandler = handler; + int savelen; + int saveprompt; + const int bq_startlinno = plinno; + char *volatile ostr = NULL; + struct parsefile *const savetopfile = getcurrentfile(); + struct heredoc *const saveheredoclist = heredoclist; + struct heredoc *here; + + str = NULL; + if (setjmp(jmploc.loc)) { + popfilesupto(savetopfile); + if (str) + ckfree(str); + if (ostr) + ckfree(ostr); + heredoclist = saveheredoclist; + handler = savehandler; + if (exception == EXERROR) { + startlinno = bq_startlinno; + synerror("Error in command substitution"); + } + longjmp(handler->loc, 1); + } + INTOFF; + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + memcpy(str, stackblock(), savelen); + } + handler = &jmploc; + heredoclist = NULL; + INTON; + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + char *oout; + int c; + int olen; + + + STARTSTACKSTR(oout); + for (;;) { + if (needprompt) { + setprompt(2); + needprompt = 0; + } + switch (c = pgetc()) { + case '`': + goto done; + + case '\\': + if ((c = pgetc()) == '\n') { + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + /* + * If eating a newline, avoid putting + * the newline into the new character + * stream (via the STPUTC after the + * switch). + */ + continue; + } + if (c != '\\' && c != '`' && c != '$' + && (!dblquote || c != '"')) + STPUTC('\\', oout); + break; + + case '\n': + plinno++; + needprompt = doprompt; + break; + + case PEOF: + startlinno = plinno; + synerror("EOF in backquote substitution"); + break; + + default: + break; + } + STPUTC(c, oout); + } +done: + STPUTC('\0', oout); + olen = oout - stackblock(); + INTOFF; + ostr = ckmalloc(olen); + memcpy(ostr, stackblock(), olen); + setinputstring(ostr, 1); + INTON; + } + nlpp = pbqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + + if (oldstyle) { + saveprompt = doprompt; + doprompt = 0; + } + + n = list(0); + + if (oldstyle) + doprompt = saveprompt; + else { + if (readtoken() != TRP) + synexpect(TRP); + } + + (*nlpp)->n = n; + if (oldstyle) { + /* + * Start reading from old file again, ignoring any pushed back + * tokens left from the backquote parsing + */ + popfile(); + tokpushback = 0; + } + while (stackblocksize() <= savelen) + growstackblock(); + STARTSTACKSTR(out); + INTOFF; + if (str) { + memcpy(out, str, savelen); + STADJUST(savelen, out); + ckfree(str); + str = NULL; + } + if (ostr) { + ckfree(ostr); + ostr = NULL; + } + here = saveheredoclist; + if (here != NULL) { + while (here->next != NULL) + here = here->next; + here->next = heredoclist; + heredoclist = saveheredoclist; + } + handler = savehandler; + INTON; + if (quoted) + USTPUTC(CTLBACKQ | CTLQUOTE, out); + else + USTPUTC(CTLBACKQ, out); + return out; +} + /* * If eofmark is NULL, read a word or a redirection symbol. If eofmark @@ -880,12 +1139,10 @@ #define CHECKEND() {goto checkend; checkend_return:;} #define PARSEREDIR() {goto parseredir; parseredir_return:;} #define PARSESUB() {goto parsesub; parsesub_return:;} -#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} -#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} STATIC int -readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) +readtoken1(int firstc, char const *initialsyntax, char *eofmark, int striptabs) { int c = firstc; char *out; @@ -893,23 +1150,21 @@ char line[EOFMARKLEN + 1]; struct nodelist *bqlist; int quotef; - int dblquote; - int varnest; /* levels of variables expansion */ - int arinest; /* levels of arithmetic expansion */ - int parenlevel; /* levels of parens in arithmetic */ - int oldstyle; - char const *prevsyntax; /* syntax before arithmetic */ + int newvarnest; + int level; int synentry; + struct tokenstate state_static[MAXNEST_STATIC]; + int maxnest = MAXNEST_STATIC; + struct tokenstate *state = state_static; startlinno = plinno; - dblquote = 0; - if (syntax == DQSYNTAX) - dblquote = 1; quotef = 0; bqlist = NULL; - varnest = 0; - arinest = 0; - parenlevel = 0; + newvarnest = 0; + level = 0; + state[level].syntax = initialsyntax; + state[level].parenlevel = 0; + state[level].category = TSTATE_TOP; STARTSTACKSTR(out); loop: { /* for each line, until end of word */ @@ -917,11 +1172,11 @@ for (;;) { /* until end of line or end of word */ CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ - synentry = syntax[c]; + synentry = state[level].syntax[c]; switch(synentry) { case CNL: /* '\n' */ - if (syntax == BASESYNTAX) + if (state[level].syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); plinno++; @@ -935,7 +1190,7 @@ USTPUTC(c, out); break; case CCTL: - if (eofmark == NULL || dblquote) + if (eofmark == NULL || initialsyntax != SQSYNTAX) USTPUTC(CTLESC, out); USTPUTC(c, out); break; @@ -951,41 +1206,37 @@ else setprompt(0); } else { - if (dblquote && c != '\\' && - c != '`' && c != '$' && - (c != '"' || eofmark != NULL)) + if (state[level].syntax == DQSYNTAX && + c != '\\' && c != '`' && c != '$' && + (c != '"' || (eofmark != NULL && + newvarnest == 0)) && + (c != '}' || state[level].category != TSTATE_VAR_OLD)) USTPUTC('\\', out); if (SQSYNTAX[c] == CCTL) USTPUTC(CTLESC, out); - else if (eofmark == NULL) + else if (eofmark == NULL || + newvarnest > 0) USTPUTC(CTLQUOTEMARK, out); USTPUTC(c, out); quotef++; } break; case CSQUOTE: - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - syntax = SQSYNTAX; + USTPUTC(CTLQUOTEMARK, out); + state[level].syntax = SQSYNTAX; break; case CDQUOTE: - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - syntax = DQSYNTAX; - dblquote = 1; + USTPUTC(CTLQUOTEMARK, out); + state[level].syntax = DQSYNTAX; break; case CENDQUOTE: - if (eofmark != NULL && arinest == 0 && - varnest == 0) { + if (eofmark != NULL && newvarnest == 0) USTPUTC(c, out); - } else { - if (arinest) { - syntax = ARISYNTAX; - dblquote = 0; - } else if (eofmark == NULL) { - syntax = BASESYNTAX; - dblquote = 0; - } + else { + if (state[level].category == TSTATE_ARITH) + state[level].syntax = ARISYNTAX; + else + state[level].syntax = BASESYNTAX; quotef++; } break; @@ -993,30 +1244,33 @@ PARSESUB(); /* parse substitution */ break; case CENDVAR: /* '}' */ - if (varnest > 0) { - varnest--; + if (level > 0 && + (state[level].category == TSTATE_VAR_OLD || + state[level].category == TSTATE_VAR_NEW)) { + if (state[level].category == TSTATE_VAR_OLD) + state[level - 1].syntax = state[level].syntax; + else + newvarnest--; + level--; USTPUTC(CTLENDVAR, out); } else { USTPUTC(c, out); } break; case CLP: /* '(' in arithmetic */ - parenlevel++; + state[level].parenlevel++; USTPUTC(c, out); break; case CRP: /* ')' in arithmetic */ - if (parenlevel > 0) { + if (state[level].parenlevel > 0) { USTPUTC(c, out); - --parenlevel; + --state[level].parenlevel; } else { if (pgetc() == ')') { - if (--arinest == 0) { + if (level > 0 && + state[level].category == TSTATE_ARITH) { + level--; USTPUTC(CTLENDARI, out); - syntax = prevsyntax; - if (syntax == DQSYNTAX) - dblquote = 1; - else - dblquote = 0; } else USTPUTC(')', out); } else { @@ -1030,12 +1284,15 @@ } break; case CBQUOTE: /* '`' */ - PARSEBACKQOLD(); + out = parsebackq(out, &bqlist, 1, + state[level].syntax == DQSYNTAX && + (eofmark == NULL || newvarnest > 0), + state[level].syntax == DQSYNTAX || state[level].syntax == ARISYNTAX); break; case CEOF: goto endword; /* exit outer loop */ default: - if (varnest == 0) + if (level == 0) goto endword; /* exit outer loop */ USTPUTC(c, out); } @@ -1043,14 +1300,17 @@ } } endword: - if (syntax == ARISYNTAX) + if (state[level].syntax == ARISYNTAX) synerror("Missing '))'"); - if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + if (state[level].syntax != BASESYNTAX && eofmark == NULL) synerror("Unterminated quoted string"); - if (varnest != 0) { + if (state[level].category == TSTATE_VAR_OLD || + state[level].category == TSTATE_VAR_NEW) { startlinno = plinno; synerror("Missing '}'"); } + if (state != state_static) + parser_temp_free_upto(state); USTPUTC('\0', out); len = out - stackblock(); out = stackblock(); @@ -1073,7 +1333,6 @@ /* end of readtoken routine */ - /* * Check to see whether we are at the end of the here document. When this * is called, c is set to the first character of the next input line. If @@ -1190,7 +1449,11 @@ PARSEARITH(); } else { pungetc(); - PARSEBACKQNEW(); + out = parsebackq(out, &bqlist, 0, + state[level].syntax == DQSYNTAX && + (eofmark == NULL || newvarnest > 0), + state[level].syntax == DQSYNTAX || + state[level].syntax == ARISYNTAX); } } else { USTPUTC(CTLVAR, out); @@ -1245,6 +1508,8 @@ subtype = VSERROR; if (c == '}') pungetc(); + else if (c == '\n' || c == PEOF) + synerror("Unexpected end of line in substitution"); else USTPUTC(c, out); } else { @@ -1261,6 +1526,8 @@ default: p = strchr(types, c); if (p == NULL) { + if (c == '\n' || c == PEOF) + synerror("Unexpected end of line in substitution"); if (flags == VSNUL) STPUTC(':', out); STPUTC(c, out); @@ -1286,192 +1553,74 @@ pungetc(); } STPUTC('=', out); - if (subtype != VSLENGTH && (dblquote || arinest)) + if (subtype != VSLENGTH && (state[level].syntax == DQSYNTAX || + state[level].syntax == ARISYNTAX)) flags |= VSQUOTE; *(stackblock() + typeloc) = subtype | flags; - if (subtype != VSNORMAL) - varnest++; - } - goto parsesub_return; -} - - -/* - * Called to parse command substitutions. Newstyle is set if the command - * is enclosed inside $(...); nlpp is a pointer to the head of the linked - * list of commands (passed by reference), and savelen is the number of - * characters on the top of the stack which must be preserved. - */ - -parsebackq: { - struct nodelist **nlpp; - int savepbq; - union node *n; - char *volatile str; - struct jmploc jmploc; - struct jmploc *const savehandler = handler; - int savelen; - int saveprompt; - const int bq_startlinno = plinno; - - savepbq = parsebackquote; - if (setjmp(jmploc.loc)) { - if (str) - ckfree(str); - parsebackquote = 0; - handler = savehandler; - if (exception == EXERROR) { - startlinno = bq_startlinno; - synerror("Error in command substitution"); - } - longjmp(handler->loc, 1); - } - INTOFF; - str = NULL; - savelen = out - stackblock(); - if (savelen > 0) { - str = ckmalloc(savelen); - memcpy(str, stackblock(), savelen); - } - handler = &jmploc; - INTON; - if (oldstyle) { - /* We must read until the closing backquote, giving special - treatment to some slashes, and then push the string and - reread it as input, interpreting it normally. */ - char *out; - int c; - int savelen; - char *str; - - - STARTSTACKSTR(out); - for (;;) { - if (needprompt) { - setprompt(2); - needprompt = 0; + if (subtype != VSNORMAL) { + if (level + 1 >= maxnest) { + maxnest *= 2; + if (state == state_static) { + state = parser_temp_alloc( + maxnest * sizeof(*state)); + memcpy(state, state_static, + MAXNEST_STATIC * sizeof(*state)); + } else + state = parser_temp_realloc(state, + maxnest * sizeof(*state)); } - switch (c = pgetc()) { - case '`': - goto done; - - case '\\': - if ((c = pgetc()) == '\n') { - plinno++; - if (doprompt) - setprompt(2); - else - setprompt(0); - /* - * If eating a newline, avoid putting - * the newline into the new character - * stream (via the STPUTC after the - * switch). - */ - continue; - } - if (c != '\\' && c != '`' && c != '$' - && (!dblquote || c != '"')) - STPUTC('\\', out); - break; - - case '\n': - plinno++; - needprompt = doprompt; - break; - - case PEOF: - startlinno = plinno; - synerror("EOF in backquote substitution"); - break; - - default: - break; + level++; + state[level].parenlevel = 0; + if (subtype == VSMINUS || subtype == VSPLUS || + subtype == VSQUESTION || subtype == VSASSIGN) { + /* + * For operators that were in the Bourne shell, + * inherit the double-quote state. + */ + state[level].syntax = state[level - 1].syntax; + state[level].category = TSTATE_VAR_OLD; + } else { + /* + * The other operators take a pattern, + * so go to BASESYNTAX. + * Also, ' and " are now special, even + * in here documents. + */ + state[level].syntax = BASESYNTAX; + state[level].category = TSTATE_VAR_NEW; + newvarnest++; } - STPUTC(c, out); - } -done: - STPUTC('\0', out); - savelen = out - stackblock(); - if (savelen > 0) { - str = ckmalloc(savelen); - memcpy(str, stackblock(), savelen); - setinputstring(str, 1); - } - } - nlpp = &bqlist; - while (*nlpp) - nlpp = &(*nlpp)->next; - *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); - (*nlpp)->next = NULL; - parsebackquote = oldstyle; - - if (oldstyle) { - saveprompt = doprompt; - doprompt = 0; - } - - n = list(0); - - if (oldstyle) - doprompt = saveprompt; - else { - if (readtoken() != TRP) - synexpect(TRP); - } - - (*nlpp)->n = n; - if (oldstyle) { - /* - * Start reading from old file again, ignoring any pushed back - * tokens left from the backquote parsing - */ - popfile(); - tokpushback = 0; - } - while (stackblocksize() <= savelen) - growstackblock(); - STARTSTACKSTR(out); - if (str) { - memcpy(out, str, savelen); - STADJUST(savelen, out); - INTOFF; - ckfree(str); - str = NULL; - INTON; + } } - parsebackquote = savepbq; - handler = savehandler; - if (arinest || dblquote) - USTPUTC(CTLBACKQ | CTLQUOTE, out); - else - USTPUTC(CTLBACKQ, out); - if (oldstyle) - goto parsebackq_oldreturn; - else - goto parsebackq_newreturn; + goto parsesub_return; } + /* * Parse an arithmetic expansion (indicate start of one and set state) */ parsearith: { - if (++arinest == 1) { - prevsyntax = syntax; - syntax = ARISYNTAX; - USTPUTC(CTLARI, out); - if (dblquote) - USTPUTC('"',out); - else - USTPUTC(' ',out); - } else { - /* - * we collapse embedded arithmetic expansion to - * parenthesis, which should be equivalent - */ - USTPUTC('(', out); + if (level + 1 >= maxnest) { + maxnest *= 2; + if (state == state_static) { + state = parser_temp_alloc( + maxnest * sizeof(*state)); + memcpy(state, state_static, + MAXNEST_STATIC * sizeof(*state)); + } else + state = parser_temp_realloc(state, + maxnest * sizeof(*state)); } + level++; + state[level].syntax = ARISYNTAX; + state[level].parenlevel = 0; + state[level].category = TSTATE_ARITH; + USTPUTC(CTLARI, out); + if (state[level - 1].syntax == DQSYNTAX) + USTPUTC('"',out); + else + USTPUTC(' ',out); goto parsearith_return; } @@ -1516,9 +1665,9 @@ */ int -goodname(char *name) +goodname(const char *name) { - char *p; + const char *p; p = name; if (! is_name(*p)) @@ -1553,11 +1702,11 @@ STATIC void -synerror(char *msg) +synerror(const char *msg) { if (commandname) - outfmt(&errout, "%s: %d: ", commandname, startlinno); - outfmt(&errout, "Syntax error: %s\n", msg); + outfmt(out2, "%s: %d: ", commandname, startlinno); + outfmt(out2, "Syntax error: %s\n", msg); error((char *)NULL); } @@ -1569,7 +1718,10 @@ #ifndef NO_HISTORY if (!el) #endif + { out2str(getprompt(NULL)); + flushout(out2); + } } /* @@ -1582,13 +1734,14 @@ static char ps[PROMPTLEN]; char *fmt; int i, j, trim; + static char internal_error[] = ""; /* * Select prompt format. */ switch (whichprompt) { case 0: - fmt = ""; + fmt = nullstr; break; case 1: fmt = ps1val(); @@ -1597,7 +1750,7 @@ fmt = ps2val(); break; default: - return ""; + return internal_error; } /* Index: bin/sh/jobs.c =================================================================== --- bin/sh/jobs.c (revision 209146) +++ bin/sh/jobs.c (working copy) @@ -91,8 +91,9 @@ STATIC struct job *getjob(char *); STATIC pid_t dowait(int, struct job *); STATIC pid_t waitproc(int, int *); +STATIC void checkzombies(void); STATIC void cmdtxt(union node *); -STATIC void cmdputs(char *); +STATIC void cmdputs(const char *); #if JOBS STATIC void setcurjob(struct job *); STATIC void deljob(struct job *); @@ -146,7 +147,7 @@ do { /* while we are in the background */ initialpgrp = tcgetpgrp(ttyfd); if (initialpgrp < 0) { -out: out2str("sh: can't access tty; job control turned off\n"); +out: out2fmt_flush("sh: can't access tty; job control turned off\n"); mflag = 0; return; } @@ -400,7 +401,7 @@ struct job *jp; TRACE(("showjobs(%d) called\n", change)); - while (dowait(0, (struct job *)NULL) > 0); + checkzombies(); for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { if (! jp->used) continue; @@ -742,6 +743,8 @@ TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, (void *)n, mode)); INTOFF; + if (mode == FORK_BG) + checkzombies(); flushall(); pid = fork(); if (pid == -1) { @@ -757,6 +760,7 @@ TRACE(("Child shell %d\n", (int)getpid())); wasroot = rootshell; rootshell = 0; + handler = &main_handler; closescript(); INTON; clear_traps(); @@ -862,6 +866,7 @@ { #if JOBS pid_t mypgrp = getpgrp(); + int propagate_int = jp->jobctl && jp->foreground; #endif int status; int st; @@ -899,6 +904,11 @@ else CLEAR_PENDING_INT; } +#if JOBS + else if (rootshell && iflag && propagate_int && + WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) + kill(getpid(), SIGINT); +#endif INTON; return st; } @@ -1046,7 +1056,7 @@ if (jp->used == 0) continue; if (jp->state == JOBSTOPPED) { - out2str("You have stopped jobs.\n"); + out2fmt_flush("You have stopped jobs.\n"); job_warning = 2; return (1); } @@ -1055,6 +1065,15 @@ return (0); } + +STATIC void +checkzombies(void) +{ + while (njobs > 0 && dowait(0, NULL) > 0) + ; +} + + /* * Return a string identifying a command (to be printed by the * jobs command. @@ -1082,7 +1101,7 @@ { union node *np; struct nodelist *lp; - char *p; + const char *p; int i; char s[2]; @@ -1211,9 +1230,10 @@ STATIC void -cmdputs(char *s) +cmdputs(const char *s) { - char *p, *q; + const char *p; + char *q; char c; int subtype = 0; Index: bin/sh/arith.y =================================================================== --- bin/sh/arith.y (revision 209146) +++ bin/sh/arith.y (working copy) @@ -85,9 +85,9 @@ ARITH_LPAREN expr ARITH_RPAREN { $$ = $2; } | expr ARITH_OR expr - { $$ = $1 ? $1 : $3 ? $3 : 0; } | + { $$ = $1 || $3; } | expr ARITH_AND expr - { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } | + { $$ = $1 && $3; } | expr ARITH_BOR expr { $$ = $1 | $3; } | expr ARITH_BXOR expr @@ -265,7 +265,7 @@ #define YYPARSE_PARAM_TYPE arith_t * #define YYPARSE_PARAM result -char *arith_buf, *arith_startbuf; +const char *arith_buf, *arith_startbuf; int yylex(void); int yyparse(YYPARSE_PARAM_TYPE); @@ -284,10 +284,12 @@ } arith_t -arith(char *s) +arith(const char *s) { arith_t result; + struct stackmark smark; + setstackmark(&smark); arith_buf = arith_startbuf = s; INTOFF; @@ -295,11 +297,13 @@ arith_lex_reset(); /* Reprime lex. */ INTON; + popstackmark(&smark); + return result; } static void -yyerror(char *s) +yyerror(const char *s) { yyerrok; @@ -314,7 +318,7 @@ int expcmd(int argc, char **argv) { - char *p; + const char *p; char *concat; char **ap; arith_t i; @@ -354,7 +358,7 @@ printf("%d\n", exp(argv[1])); } -error(char *s) +error(const char *s) { fprintf(stderr, "exp: %s\n", s); exit(1); Index: bin/sh/var.h =================================================================== --- bin/sh/var.h (revision 209146) +++ bin/sh/var.h (working copy) @@ -77,6 +77,7 @@ extern struct var vps4; #ifndef NO_HISTORY extern struct var vhistsize; +extern struct var vterm; #endif /* @@ -96,19 +97,21 @@ #define optindval() (voptind.text + 7) #ifndef NO_HISTORY #define histsizeval() (vhistsize.text + 9) +#define termval() (vterm.text + 5) #endif #define mpathset() ((vmpath.flags & VUNSET) == 0) void initvar(void); -void setvar(char *, char *, int); +void setvar(const char *, const char *, int); void setvareq(char *, int); struct strlist; void listsetvar(struct strlist *); -char *lookupvar(char *); -char *bltinlookup(char *, int); +char *lookupvar(const char *); +char *bltinlookup(const char *, int); +void bltinsetlocale(void); +void bltinunsetlocale(void); char **environment(void); -void shprocvar(void); int showvarscmd(int, char **); int exportcmd(int, char **); int localcmd(int, char **); @@ -116,5 +119,5 @@ void poplocalvars(void); int setvarcmd(int, char **); int unsetcmd(int, char **); -int unsetvar(char *); -int setvarsafe(char *, char *, int); +int unsetvar(const char *); +int setvarsafe(const char *, const char *, int);