zsh-workers
 help / color / mirror / code / Atom feed
* Running "unset path" breaks PATH despite emulation being enabled
@ 2017-09-08  8:29 Eric Pruitt
  2017-09-08 11:30 ` Mikael Magnusson
  0 siblings, 1 reply; 5+ messages in thread
From: Eric Pruitt @ 2017-09-08  8:29 UTC (permalink / raw)
  To: zsh-workers

According and <http://www.zsh.org/mla/users/2015/msg00180.html> and the
manual, Z shell should become roughly POSIX compatible when using
"emulate sh". When using emulation, running 'path=""' does not break
PATH command execution, but running 'unset path' still does which seems
like a bug to me:

    $ zsh -c 'emulate -L sh; path=""; ls /dev/null'
    /dev/null
    $ zsh -c 'emulate -L sh; unset path; ls /dev/null'
    zsh:1: command not found: ls

Eric


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Running "unset path" breaks PATH despite emulation being enabled
  2017-09-08  8:29 Running "unset path" breaks PATH despite emulation being enabled Eric Pruitt
@ 2017-09-08 11:30 ` Mikael Magnusson
  2017-09-08 16:17   ` Peter Stephenson
  2017-09-09  4:24   ` Eric Pruitt
  0 siblings, 2 replies; 5+ messages in thread
From: Mikael Magnusson @ 2017-09-08 11:30 UTC (permalink / raw)
  To: Eric Pruitt; +Cc: zsh workers

On Fri, Sep 8, 2017 at 10:29 AM, Eric Pruitt <eric.pruitt@gmail.com> wrote:
> According and <http://www.zsh.org/mla/users/2015/msg00180.html> and the
> manual, Z shell should become roughly POSIX compatible when using
> "emulate sh". When using emulation, running 'path=""' does not break
> PATH command execution, but running 'unset path' still does which seems
> like a bug to me:
>
>     $ zsh -c 'emulate -L sh; path=""; ls /dev/null'
>     /dev/null
>     $ zsh -c 'emulate -L sh; unset path; ls /dev/null'
>     zsh:1: command not found: ls

This works fine if you start zsh as sh though, ie either make a
symlink ln -s =zsh sh; ./sh or do ARGV0=sh zsh
It can't really work in the 'emulate sh' case since the parameter
$path is then already created and it would be quite controversial for
emulate to remove parameters from the shell environment.

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Running "unset path" breaks PATH despite emulation being enabled
  2017-09-08 11:30 ` Mikael Magnusson
@ 2017-09-08 16:17   ` Peter Stephenson
  2017-09-11  8:58     ` Peter Stephenson
  2017-09-09  4:24   ` Eric Pruitt
  1 sibling, 1 reply; 5+ messages in thread
From: Peter Stephenson @ 2017-09-08 16:17 UTC (permalink / raw)
  To: Zsh Hackers' List

On Fri, 8 Sep 2017 13:30:05 +0200
Mikael Magnusson <mikachu@gmail.com> wrote:
> >     $ zsh -c 'emulate -L sh; path=""; ls /dev/null'
> >     /dev/null
> >     $ zsh -c 'emulate -L sh; unset path; ls /dev/null'
> >     zsh:1: command not found: ls
> 
> This works fine if you start zsh as sh though, ie either make a
> symlink ln -s =zsh sh; ./sh or do ARGV0=sh zsh
> It can't really work in the 'emulate sh' case since the parameter
> $path is then already created and it would be quite controversial for
> emulate to remove parameters from the shell environment.

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
..."?

pws

diff --git a/Doc/Zsh/invoke.yo b/Doc/Zsh/invoke.yo
index e03c1e2..da09c0f 100644
--- a/Doc/Zsh/invoke.yo
+++ b/Doc/Zsh/invoke.yo
@@ -46,6 +46,16 @@ 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.
+
 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..cee12ac 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -244,10 +244,13 @@ 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;
@@ -270,7 +273,7 @@ parseargs(char **argv, char **runscript, char **cmdptr)
     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 +337,27 @@ parseopts_insert(LinkList optlist, char *base, int optno)
 }
 
 /*
+ * This sets the global emulation plus the options we traditionally
+ * set immediately after that.  This is jsut 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());
+}
+
+/*
  * 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 +366,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 +405,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 +418,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 +439,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 +462,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 +492,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 +526,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 +1720,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


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Running "unset path" breaks PATH despite emulation being enabled
  2017-09-08 11:30 ` Mikael Magnusson
  2017-09-08 16:17   ` Peter Stephenson
@ 2017-09-09  4:24   ` Eric Pruitt
  1 sibling, 0 replies; 5+ messages in thread
From: Eric Pruitt @ 2017-09-09  4:24 UTC (permalink / raw)
  To: zsh workers

On Fri, Sep 08, 2017 at 01:30:05PM +0200, Mikael Magnusson wrote:
> This works fine if you start zsh as sh though, ie [...] ARGV0=sh zsh

For the benefit of other people who like myself, don't use actually Z
shell, this is NOT the equivalent of "ARGV0=sh zsh" in many (most?)
other shells, it's describing execve("sh", ["zsh", ...]):

    # For POSIX shells, you can use Z shell itself assuming there's not
    # a standardized built-in way I overlooked:
    $ zsh -c "ARGV0=sh zsh ..."

    # For Bash (and some others):
    $ (exec -a sh zsh ...)

Eric


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Running "unset path" breaks PATH despite emulation being enabled
  2017-09-08 16:17   ` Peter Stephenson
@ 2017-09-11  8:58     ` Peter Stephenson
  0 siblings, 0 replies; 5+ messages in thread
From: Peter Stephenson @ 2017-09-11  8:58 UTC (permalink / raw)
  To: Zsh Hackers' List

On Fri, 8 Sep 2017 17:17:03 +0100
Peter Stephenson <p.stephenson@samsung.com> 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


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-09-11  8:58 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-09-08  8:29 Running "unset path" breaks PATH despite emulation being enabled Eric Pruitt
2017-09-08 11:30 ` Mikael Magnusson
2017-09-08 16:17   ` Peter Stephenson
2017-09-11  8:58     ` Peter Stephenson
2017-09-09  4:24   ` Eric Pruitt

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).