diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | Doc/Zsh/builtins.yo | 9 | ||||
-rw-r--r-- | Doc/Zsh/grammar.yo | 53 | ||||
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | Src/exec.c | 111 | ||||
-rw-r--r-- | Src/zsh.h | 1 | ||||
-rw-r--r-- | Test/A01grammar.ztst | 16 |
7 files changed, 169 insertions, 35 deletions
diff --git a/ChangeLog b/ChangeLog index cd8c11c01..554a6154d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2007-05-08 Peter Stephenson <pws@csr.com> + + * Phil Pennock + tweaks: 23398 + more tweaks: README, + Doc/Zsh/builtins.yo, Doc/Zsh/grammar.yo, Src/exec.c, Src/zsh.h, + Test/A01grammar.ztst: add compatibility options for exec. + 2007-05-02 Peter Stephenson <pws@csr.com> * unposted: Doc/Zsh/mod_pcre.yo, Src/pcre.c: missing diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index dde13b47a..0f90bca6d 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -6,7 +6,6 @@ sect(Shell Builtin Commands) cindex(builtin commands) cindex(commands, builtin) def(prefix)(1)(\ -findex(ARG1) item(tt(ARG1) var(simple command))( See noderef(Precommand Modifiers). )\ @@ -375,7 +374,13 @@ item(tt(eval) [ var(arg) ... ])( Read the arguments as input to the shell and execute the resulting command in the current shell process. ) -prefix(exec) +item(tt(exec) [ tt(-cl) ] [ tt(-a) var(argv0) ] var(simple command))( +Replace the current shell with an external command rather than forking. +With tt(-c) clear the environment; with tt(-l) prepend tt(-) to the +tt(argv[0]) string of the command executed (to simulate a login shell); +with tt(-a) var(argv0) set the tt(argv[0]) string of the command +executed. See noderef(Precommand Modifiers). +) findex(exit) item(tt(exit) [ var(n) ])( Exit the shell with the exit status specified by var(n); if none diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo index 6138a8b9b..0c2aee960 100644 --- a/Doc/Zsh/grammar.yo +++ b/Doc/Zsh/grammar.yo @@ -105,29 +105,56 @@ shell builtin commands with the exception of tt(nocorrect) which is a reserved word. startitem() +findex(-) item(tt(-))( The command is executed with a `tt(-)' prepended to its tt(argv[0]) string. ) -item(tt(noglob))( -Filename generation (globbing) is not performed on any of -the words. +findex(builtin) +item(tt(builtin))( +The command word is taken to be the name of a builtin command, +rather than a shell function or external command. ) +findex(command) +item(tt(command) [ tt(-pvV) ])( +The command word is taken to be the name of an external command, +rather than a shell function or builtin. If the tt(POSIX_BUILTINS) option +is set, builtins will also be executed but certain special properties +of them are suppressed. The tt(-p) flag causes a default path to be +searched instead of that in tt($path). With the tt(-v) flag, tt(command) +is similar to tt(whence) and with tt(-V), it is equivalent to tt(whence +-v). +) +findex(exec) +item(tt(exec) [ tt(-cl) ] [ tt(-a) var(argv0) ])( +The following command together with any arguments is run in place +of the current process, rather than as a sub-process. The shell does not +fork and is replaced. The shell does not invoke tt(TRAPEXIT), nor does it +source tt(zlogout) files. +The options are provided for compatibility with other shells. + +The tt(-c) option clears the environment. + +The tt(-l) option is equivalent to the tt(-) precommand modifier, to +treat the replacement command as a login shell; the command is executed +with a tt(-) prepended to its tt(argv[0]) string. This flag has no effect +if used together with the tt(-a) option. + +The tt(-a) option is used to specify explicitly the tt(argv[0]) string +(the name of the command as seen by the process itself) to be used by the +replacement command and is directly equivalent to setting a value +for the tt(ARGV0) environment variable. +) +findex(nocorrect) item(tt(nocorrect))( Spelling correction is not done on any of the words. This must appear before any other precommand modifier, as it is interpreted immediately, before any parsing is done. It has no effect in non-interactive shells. ) -item(tt(exec))( -The command is executed in the parent shell without forking. -) -item(tt(command))( -The command word is taken to be the name of an external command, -rather than a shell function or builtin. -) -item(tt(builtin))( -The command word is taken to be the name of a builtin command, -rather than a shell function or external command. +findex(noglob) +item(tt(noglob))( +Filename generation (globbing) is not performed on any of +the words. ) enditem() texinode(Complex Commands)(Alternate Forms For Complex Commands)(Precommand Modifiers)(Shell Grammar) diff --git a/README b/README index e01bc81ec..94ba2236c 100644 --- a/README +++ b/README @@ -40,6 +40,14 @@ behaviour.) Now it is treated identically to "$@". The same change applies to expressions with forced splitting such as ${=1+"$@"}, but otherwise the case where SH_WORD_SPLIT is not set is unaffected. +The "exec" precommand modifier now takes various options for compatibility +with other shells. This means that whereas "exec -prog" previously +tried to execute a command name "-prog", it will now report an error +in option handling. "exec -- -prog" will execute "-prog". If +the option EQUALS is set, as it is by default in zsh's native mode, +"exec =-prog" behaves the same way in all versions of zsh provided +the command can be found. + The "unset" builtin now does not regard the unsetting of non-existent variables as an error, so can still return status 0 (depending on the handling of other arguments). This appears to be the standard shell diff --git a/Src/exec.c b/Src/exec.c index cdd765113..a462a4ee9 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -144,6 +144,7 @@ mod_export Funcstack funcstack; static int doneps4; static char *STTYval; +static char *blank_env[] = { NULL }; /* Execution functions. */ @@ -361,7 +362,7 @@ execcursh(Estate state, int do_exec) /**/ static int -zexecve(char *pth, char **argv) +zexecve(char *pth, char **argv, char **newenvp) { int eno; static char buf[PATH_MAX * 2]; @@ -379,7 +380,10 @@ zexecve(char *pth, char **argv) sprintf(buf + 2, "%s/%s", pwd, pth); zputenv(buf); closedumps(); - execve(pth, argv, environ); + + if (newenvp == NULL) + newenvp = environ; + execve(pth, argv, newenvp); /* If the execve returns (which in general shouldn't happen), * * then check for an errno equal to ENOEXEC. This errno is set * @@ -414,14 +418,14 @@ zexecve(char *pth, char **argv) *ptr = '\0'; argv[-2] = ptr2; argv[-1] = ptr + 1; - execve(ptr2, argv - 2, environ); + execve(ptr2, argv - 2, newenvp); } else { argv[-1] = ptr2; - execve(ptr2, argv - 1, environ); + execve(ptr2, argv - 1, newenvp); } } else if (eno == ENOEXEC) { argv[-1] = "sh"; - execve("/bin/sh", argv - 1, environ); + execve("/bin/sh", argv - 1, newenvp); } } else if (eno == ENOEXEC) { for (t0 = 0; t0 != ct; t0++) @@ -429,7 +433,7 @@ zexecve(char *pth, char **argv) break; if (t0 == ct) { argv[-1] = "sh"; - execve("/bin/sh", argv - 1, environ); + execve("/bin/sh", argv - 1, newenvp); } } } else @@ -467,13 +471,13 @@ isgooderr(int e, char *dir) /* execute an external command */ /**/ -void -execute(LinkList args, int dash, int defpath) +static void +execute(LinkList args, int flags, int defpath) { Cmdnam cn; char buf[MAXCMDLEN], buf2[MAXCMDLEN]; char *s, *z, *arg0; - char **argv, **pp; + char **argv, **pp, **newenvp = NULL; int eno = 0, ee; arg0 = (char *) peekfirst(args); @@ -502,7 +506,7 @@ execute(LinkList args, int dash, int defpath) if (unset(RESTRICTED) && (z = zgetenv("ARGV0"))) { setdata(firstnode(args), (void *) ztrdup(z)); delenvvalue(z - 6); - } else if (dash) { + } else if (flags & BINF_DASH) { /* Else if the pre-command `-' was given, we add `-' * * to the front of argv[0] for this command. */ sprintf(buf2, "-%s", arg0); @@ -510,6 +514,9 @@ execute(LinkList args, int dash, int defpath) } argv = makecline(args); + if (flags & BINF_CLEARENV) + newenvp = blank_env; + /* * Note that we don't close fd's attached to process substitution * here, which should be visible to external processes. @@ -522,7 +529,7 @@ execute(LinkList args, int dash, int defpath) } for (s = arg0; *s; s++) if (*s == '/') { - int lerrno = zexecve(arg0, argv); + int lerrno = zexecve(arg0, argv, newenvp); if (arg0 == s || unset(PATHDIRS) || (arg0[0] == '.' && (arg0 + 1 == s || (arg0[1] == '.' && arg0 + 2 == s)))) { @@ -559,7 +566,7 @@ execute(LinkList args, int dash, int defpath) _exit(127); } - ee = zexecve(pbuf, argv); + ee = zexecve(pbuf, argv, newenvp); if ((dptr = strrchr(pbuf, '/'))) *dptr = '\0'; @@ -576,7 +583,7 @@ execute(LinkList args, int dash, int defpath) else { for (pp = path; pp < cn->u.name; pp++) if (!**pp || (**pp == '.' && (*pp)[1] == '\0')) { - ee = zexecve(arg0, argv); + ee = zexecve(arg0, argv, newenvp); if (isgooderr(ee, *pp)) eno = ee; } else if (**pp != '/') { @@ -584,7 +591,7 @@ execute(LinkList args, int dash, int defpath) strucpy(&z, *pp); *z++ = '/'; strcpy(z, arg0); - ee = zexecve(buf, argv); + ee = zexecve(buf, argv, newenvp); if (isgooderr(ee, *pp)) eno = ee; } @@ -592,7 +599,7 @@ execute(LinkList args, int dash, int defpath) strcat(nn, "/"); strcat(nn, cn->node.nam); } - ee = zexecve(nn, argv); + ee = zexecve(nn, argv, newenvp); if ((dptr = strrchr(nn, '/'))) *dptr = '\0'; @@ -601,7 +608,7 @@ execute(LinkList args, int dash, int defpath) } for (pp = path; *pp; pp++) if (!(*pp)[0] || ((*pp)[0] == '.' && !(*pp)[1])) { - ee = zexecve(arg0, argv); + ee = zexecve(arg0, argv, newenvp); if (isgooderr(ee, *pp)) eno = ee; } else { @@ -609,7 +616,7 @@ execute(LinkList args, int dash, int defpath) strucpy(&z, *pp); *z++ = '/'; strcpy(z, arg0); - ee = zexecve(buf, argv); + ee = zexecve(buf, argv, newenvp); if (isgooderr(ee, *pp)) eno = ee; } @@ -2005,7 +2012,7 @@ execcmd(Estate state, int input, int output, int how, int last1) cflags &= ~BINF_BUILTIN & ~BINF_COMMAND; cflags |= hn->flags; checked = 0; - if (cflags & BINF_COMMAND && nextnode(firstnode(args))) { + if ((cflags & BINF_COMMAND) && nextnode(firstnode(args))) { /* check for options to command builtin */ char *next = (char *) getdata(nextnode(firstnode(args))); char *cmdopt; @@ -2024,7 +2031,71 @@ execcmd(Estate state, int input, int output, int how, int last1) } } if (!strcmp(next, "--")) - uremnode(args, firstnode(args)); + uremnode(args, firstnode(args)); + } + if ((cflags & BINF_EXEC) && nextnode(firstnode(args))) { + /* + * Check for compatibility options to exec builtin. + * It would be nice to do these more generically, + * but currently we don't have a mechanism for + * precommand modifiers. + */ + char *next = (char *) getdata(nextnode(firstnode(args))); + char *cmdopt, *exec_argv0 = NULL; + /* + * Careful here: we want to make sure a final dash + * is passed through in order that it still behaves + * as a precommand modifier (zsh equivalent of -l). + * It has to be last, but I think that's OK since + * people aren't likely to mix the option style + * with the zsh style. + */ + while (next && *next == '-' && strlen(next) >= 2) { + uremnode(args, firstnode(args)); + if (!strcmp(next, "--")) + break; + for (cmdopt = &next[1]; *cmdopt; ++cmdopt) { + switch (*cmdopt) { + case 'a': + /* argument is ARGV0 string */ + if (cmdopt[1]) { + exec_argv0 = cmdopt+1; + /* position on last non-NULL character */ + cmdopt += strlen(cmdopt+1); + } else { + if (!nextnode(firstnode(args))) { + zerr("exec flag -a requires a parameter"); + errflag = lastval = 1; + return; + } + exec_argv0 = (char *) + getdata(nextnode(firstnode(args))); + uremnode(args, firstnode(args)); + } + break; + case 'c': + cflags |= BINF_CLEARENV; + break; + case 'l': + cflags |= BINF_DASH; + break; + default: + zerr("unknown exec flag -%c", *cmdopt); + errflag = lastval = 1; + return; + } + } + next = (char *) getdata(nextnode(firstnode(args))); + } + if (exec_argv0) { + char *str, *s; + size_t sz = strlen(exec_argv0); + str = s = zalloc(5 + 1 + sz + 1); + strcpy(s, "ARGV0="); + s+=6; + strcpy(s, exec_argv0); + zputenv(str); + } } uremnode(args, firstnode(args)); hn = NULL; @@ -2726,7 +2797,7 @@ execcmd(Estate state, int input, int output, int how, int last1) zsfree(STTYval); STTYval = 0; } - execute(args, cflags & BINF_DASH, use_defpath); + execute(args, cflags, use_defpath); } else { /* ( ... ) */ DPUTS(varspc, "BUG: assignment before complex command"); diff --git a/Src/zsh.h b/Src/zsh.h index 0d475441a..a80a6fd99 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1144,6 +1144,7 @@ struct builtin { #define BINF_KEEPNUM (1<<13) /* `[-+]NUM' can be an option */ #define BINF_SKIPDASH (1<<14) /* Treat `-' as argument (maybe `+') */ #define BINF_DASHDASHVALID (1<<15) /* Handle `--' even if SKIPINVALD */ +#define BINF_CLEARENV (1<<16) /* new process started with cleared env */ struct module { char *nam; diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 42ff8d405..22b6f9c88 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -62,6 +62,22 @@ (exec /bin/sh; echo bar) 0:`exec' precommand modifier + (exec -l /bin/sh -c 'echo $0') +0:`exec' with -l option +>-/bin/sh + + (exec -a /bin/SPLATTER /bin/sh -c 'echo $0') +0:`exec' with -a option +>/bin/SPLATTER + + (exec -a/bin/SPLOOSH /bin/sh -c 'echo $0') +0:`exec' with -a option, no space +>/bin/SPLOOSH + + (export FOO=bar; exec -c /bin/sh -c 'echo x${FOO}x') +0:`exec' with -c option +>xx + cat() { echo Function cat executed; } command cat && unfunction cat 0:`command' precommand modifier |