Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: Running "unset path" breaks PATH despite emulation being enabled
On Fri, 8 Sep 2017 17:17:03 +0100
Peter Stephenson <p.stephenson@xxxxxxxxxxx> wrote:
> All this is entirely correct. I wonder if it would be helpful for us to
> make it easier to set an emulation at invocation, i.e. "zsh --emulate sh
> ..."?
I've slightly reordered to try to ensure the maximum compatibility with
the previous code, and also added an extra sentence about the usefulness
of --emulate compared with the emulate builtin. I'll commit this.
pws
diff --git a/Doc/Zsh/invoke.yo b/Doc/Zsh/invoke.yo
index e03c1e2..a184bc8 100644
--- a/Doc/Zsh/invoke.yo
+++ b/Doc/Zsh/invoke.yo
@@ -46,6 +46,20 @@ ifzman(zmanref(zshoptions))\
ifnzman(noderef(Options))\
.
+The long option `tt(--emulate)' followed (in a separate word) by an
+emulation mode may be passed to the shell.
+The emulation modes are those described for the tt(emulate) builtin,
+see
+ifzman(zmanref(zshbuiltins))\
+ifnzman(noderef(Shell Builtin Commands)).
+The `tt(--emulate)' option must precede any other options (which might
+otherwise be overridden), but following options are honoured, so
+may be used to modify the requested emulation mode. Note that certain
+extra steps are taken to ensure a smooth emulation when this option
+is used compared with the tt(emulate) command within the shell: for
+example, variables that conflict with POSIX usage such as tt(path) are
+not defined within the shell.
+
Options may be specified by name using the tt(-o) option. tt(-o) acts like
a single-letter option, but takes a following string as the option name.
For example,
diff --git a/Src/builtin.c b/Src/builtin.c
index 2e72ba2..0c2a62a 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5905,7 +5905,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func))
savehackchar = keyboardhackchar;
emulate(shname, opt_R, &new_emulation, new_opts);
optlist = newlinklist();
- if (parseopts(nam, &argv, new_opts, &cmd, optlist)) {
+ if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) {
ret = 1;
goto restore;
}
diff --git a/Src/init.c b/Src/init.c
index 87dd2e2..c537266 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -244,33 +244,24 @@ static int restricted;
/**/
static void
-parseargs(char **argv, char **runscript, char **cmdptr)
+parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr)
{
char **x;
LinkList paramlist;
+ int flags = PARSEARGS_TOPLEVEL;
+ if (**argv == '-')
+ flags |= PARSEARGS_LOGIN;
argzero = posixzero = *argv++;
SHIN = 0;
- /* There's a bit of trickery with opts[INTERACTIVE] here. It starts *
- * at a value of 2 (instead of 1) or 0. If it is explicitly set on *
- * the command line, it goes to 1 or 0. If input is coming from *
- * somewhere that normally makes the shell non-interactive, we do *
- * "opts[INTERACTIVE] &= 1", so that only a *default* on state will *
- * be changed. At the end of the function, a value of 2 gets *
- * changed to 1. */
- opts[INTERACTIVE] = isatty(0) ? 2 : 0;
/*
- * MONITOR is similar: we initialise it to 2, and if it's
- * still 2 at the end, we set it to the value of INTERACTIVE.
+ * parseopts sets up some options after we deal with emulation in
+ * order to be consistent --- the code in parseopts_setemulate() is
+ * matched by code at the end of the present function.
*/
- opts[MONITOR] = 2; /* may be unset in init_io() */
- opts[HASHDIRS] = 2; /* same relationship to INTERACTIVE */
- opts[USEZLE] = 1; /* see below, related to SHINSTDIN */
- opts[SHINSTDIN] = 0;
- opts[SINGLECOMMAND] = 0;
- if (parseopts(NULL, &argv, opts, cmdptr, NULL))
+ if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags))
exit(1);
/*
@@ -334,9 +325,45 @@ parseopts_insert(LinkList optlist, char *base, int optno)
}
/*
+ * This sets the global emulation plus the options we traditionally
+ * set immediately after that. This is just for historical consistency
+ * --- I don't think those options actually need to be set here.
+ */
+static void parseopts_setemulate(char *nam, int flags)
+{
+ emulate(nam, 1, &emulation, opts); /* initialises most options */
+ opts[LOGINSHELL] = ((flags & PARSEARGS_LOGIN) != 0);
+ opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
+
+ /* There's a bit of trickery with opts[INTERACTIVE] here. It starts *
+ * at a value of 2 (instead of 1) or 0. If it is explicitly set on *
+ * the command line, it goes to 1 or 0. If input is coming from *
+ * somewhere that normally makes the shell non-interactive, we do *
+ * "opts[INTERACTIVE] &= 1", so that only a *default* on state will *
+ * be changed. At the end of the function, a value of 2 gets *
+ * changed to 1. */
+ opts[INTERACTIVE] = isatty(0) ? 2 : 0;
+ /*
+ * MONITOR is similar: we initialise it to 2, and if it's
+ * still 2 at the end, we set it to the value of INTERACTIVE.
+ */
+ opts[MONITOR] = 2; /* may be unset in init_io() */
+ opts[HASHDIRS] = 2; /* same relationship to INTERACTIVE */
+ opts[USEZLE] = 1; /* see below, related to SHINSTDIN */
+ opts[SHINSTDIN] = 0;
+ opts[SINGLECOMMAND] = 0;
+}
+
+/*
* Parse shell options.
- * If nam is not NULL, this is called from a command; don't
- * exit on failure.
+ *
+ * If (flags & PARSEARGS_TOPLEVEL):
+ * - we are doing shell initilisation
+ * - nam is the name under which the shell was started
+ * - set up emulation and standard options based on that.
+ * Otherwise:
+ * - nam is a command name
+ * - don't exit on failure.
*
* If optlist is not NULL, it used to form a list of pointers
* into new_opts indicating which options have been changed.
@@ -345,23 +372,26 @@ parseopts_insert(LinkList optlist, char *base, int optno)
/**/
mod_export int
parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
- LinkList optlist)
+ LinkList optlist, int flags)
{
int optionbreak = 0;
int action, optno;
char **argv = *argvp;
+ int toplevel = ((flags & PARSEARGS_TOPLEVEL) != 0u);
+ int emulate_required = toplevel;
+ char *top_emulation = nam;
*cmdp = 0;
#define WARN_OPTION(F, S) \
do { \
- if (nam) \
+ if (!toplevel) \
zwarnnam(nam, F, S); \
else \
zerr(F, S); \
} while (0)
#define LAST_OPTION(N) \
do { \
- if (nam) { \
+ if (!toplevel) { \
if (*argv) \
argv++; \
goto doneargv; \
@@ -381,7 +411,7 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
argv++;
goto doneoptions;
}
- if (nam || *argv != args+1 || **argv != '-')
+ if (!toplevel || *argv != args+1 || **argv != '-')
goto badoptionstring;
/* GNU-style long options */
++*argv;
@@ -394,6 +424,19 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
printhelp();
LAST_OPTION(0);
}
+ if (!strcmp(*argv, "emulate")) {
+ ++argv;
+ if (!*argv) {
+ zerr("--emulate: argument required");
+ exit(1);
+ }
+ if (!emulate_required) {
+ zerr("--emulate: must precede other options");
+ exit(1);
+ }
+ top_emulation = *argv;
+ break;
+ }
/* `-' characters are allowed in long options */
for(args = *argv; *args; args++)
if(*args == '-')
@@ -402,9 +445,17 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
}
if (unset(SHOPTIONLETTERS) && **argv == 'b') {
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
/* -b ends options at the end of this argument */
optionbreak = 1;
} else if (**argv == 'c') {
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
/* -c command */
*cmdp = *argv;
new_opts[INTERACTIVE] &= 1;
@@ -417,15 +468,20 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
return 1;
}
longoptions:
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
if (!(optno = optlookup(*argv))) {
WARN_OPTION("no such option: %s", *argv);
return 1;
- } else if (optno == RESTRICTED && !nam) {
+ } else if (optno == RESTRICTED && toplevel) {
restricted = action;
- } else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
+ } else if ((optno == EMACSMODE || optno == VIMODE) && !toplevel) {
WARN_OPTION("can't change option: %s", *argv);
} else {
- if (dosetopt(optno, action, !nam, new_opts) && nam) {
+ if (dosetopt(optno, action, toplevel, new_opts) &&
+ !toplevel) {
WARN_OPTION("can't change option: %s", *argv);
} else if (optlist) {
parseopts_insert(optlist, new_opts, optno);
@@ -442,15 +498,21 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
}
break;
} else {
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
if (!(optno = optlookupc(**argv))) {
WARN_OPTION("bad option: -%c", **argv);
return 1;
- } else if (optno == RESTRICTED && !nam) {
+ } else if (optno == RESTRICTED && toplevel) {
restricted = action;
- } else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
+ } else if ((optno == EMACSMODE || optno == VIMODE) &&
+ !toplevel) {
WARN_OPTION("can't change option: %s", *argv);
} else {
- if (dosetopt(optno, action, !nam, new_opts) && nam) {
+ if (dosetopt(optno, action, toplevel, new_opts) &&
+ !toplevel) {
WARN_OPTION("can't change option: -%c", **argv);
} else if (optlist) {
parseopts_insert(optlist, new_opts, optno);
@@ -470,6 +532,10 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
}
doneargv:
*argvp = argv;
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
return 0;
}
@@ -1660,11 +1726,9 @@ zsh_main(UNUSED(int argc), char **argv)
fdtable[0] = fdtable[1] = fdtable[2] = FDT_EXTERNAL;
createoptiontable();
- emulate(zsh_name, 1, &emulation, opts); /* initialises most options */
- opts[LOGINSHELL] = (**argv == '-');
- opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
- /* sets ZLE, INTERACTIVE, SHINSTDIN and SINGLECOMMAND */
- parseargs(argv, &runscript, &cmd);
+ /* sets emulation, LOGINSHELL, PRIVILEGED, ZLE, INTERACTIVE,
+ * SHINSTDIN and SINGLECOMMAND */
+ parseargs(zsh_name, argv, &runscript, &cmd);
SHTTY = -1;
init_io(cmd);
diff --git a/Src/zsh.h b/Src/zsh.h
index abe9a9c..1e982a6 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1361,6 +1361,14 @@ struct options {
int argscount, argsalloc;
};
+/* Flags to parseargs() */
+
+enum {
+ PARSEARGS_TOPLEVEL = 0x1, /* Call to initialise shell */
+ PARSEARGS_LOGIN = 0x2 /* Shell is login shell */
+};
+
+
/*
* Handler arguments are: builtin name, null-terminated argument
* list excluding command name, option structure, the funcid element from the
diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst
index 2de097e..7b1592f 100644
--- a/Test/B07emulate.ztst
+++ b/Test/B07emulate.ztst
@@ -251,3 +251,28 @@
emulate sh -c '[[ a == a ]]'
0:regression test for POSIX_ALIASES reserved words
F:Some reserved tokens are handled in alias expansion
+
+ for mode in ksh bash zsh; do
+ $ZTST_testdir/../Src/zsh --emulate $mode -f -c 'emulate'
+ done
+0:--emulate option
+>ksh
+>sh
+>zsh
+
+ $ZTST_testdir/../Src/zsh -f --emulate sh
+1:--emulate must be first
+*?*: --emulate: must precede other options
+
+ $ZTST_testdir/../Src/zsh --emulate
+1:--emulate needs an argument
+*?*: --emulate: argument required
+
+ for opt in shwordsplit noshwordsplit; do
+ $ZTST_testdir/../Src/zsh --emulate sh -f -o $opt -c '
+ [[ -o shwordsplit ]] && echo yes || echo no
+ '
+ done
+0:--emulate followed by other options
+>yes
+>no
Messages sorted by:
Reverse Date,
Date,
Thread,
Author