zsh-workers
 help / color / mirror / code / Atom feed
* emulate with options other than -c is broken
@ 2012-10-05 23:01 Peter Stephenson
  2012-10-07 19:26 ` Peter Stephenson
  0 siblings, 1 reply; 4+ messages in thread
From: Peter Stephenson @ 2012-10-05 23:01 UTC (permalink / raw)
  To: Zsh Hackers' List

Regardless of the autoload thing,

  emulate zsh -o extendedglob -c '[[ -o extendedglob ]] ||
  print "extendedglob is NOT set"'

prints

  extendedglob is NOT set

(though zsh emulation did take effect) while

  setopt ignorebraces          
  emulate zsh -o extendedglob
  [[ -o ignorebraces ]] && print "ignorebraces IS still set"

prints

  ignorebraces IS still set

(though extendedglob did get set).

This is because of the position where the emulation takes effect within
the emulate builtin.  Also, I'm not happy that parse errors in the
option parsing are handled properly.

Work for tomorrow.

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


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

* Re: emulate with options other than -c is broken
  2012-10-05 23:01 emulate with options other than -c is broken Peter Stephenson
@ 2012-10-07 19:26 ` Peter Stephenson
  2012-10-11 19:53   ` Peter Stephenson
  0 siblings, 1 reply; 4+ messages in thread
From: Peter Stephenson @ 2012-10-07 19:26 UTC (permalink / raw)
  To: Zsh Hackers' List

On Sat, 6 Oct 2012 00:01:25 +0100
Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> Regardless of the autoload thing,
> 
>   emulate zsh -o extendedglob -c '[[ -o extendedglob ]] ||
>   print "extendedglob is NOT set"'
> 
> prints
> 
>   extendedglob is NOT set
> 
> (though zsh emulation did take effect) while
> 
>   setopt ignorebraces          
>   emulate zsh -o extendedglob
>   [[ -o ignorebraces ]] && print "ignorebraces IS still set"
> 
> prints
> 
>   ignorebraces IS still set
> 
> (though extendedglob did get set).
>
> Also, I'm not happy that parse errors in the option parsing are
> handled properly.

This should fix and test all three.

There's one thing still only half fixed:

emulate zsh -o extendedglob -c 'func() { [[ -o extendedglob ]] && print is on }'
func

doesn't print "is on" yet.  This sure as heck looks like a bug: if you
get to pass options uniformly with the emulation, why would the
emulation but not the options be sticky?  (It's half fixed because
I've created and initialised a sticky_opts array but not used it.)

This will mean storing the full set of options with the function
definition.  I'd propose to do that compactly, i.e. as a bit array
rather than a char array (consuming 22 characters, it would seem, which
doesn't strike me as excessive --- I can restrict it to when sticky
emulation is in effect).

Open question: where else would it be sensible to use a bit array rather
than a char array for options?  In doshfunc we save "char
saveopts[OPT_SIZE]" (OPT_SIZE appears to be 170) for every function on
the stack, which seems pretty wasteful, but I'm not 100% sure, given the
size of memory on modern systems, whether compacting this is worth the
extra time taken.

(I thought of always maintaining both the non-compact and compact forms,
since the compact form is 8 times smaller; then we only need to loop
over all options when we restore from a compact copy, not when we save
which can just copy the current compact form.  I think this is workable
but it might be more than we really need.)

Index: Src/builtin.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/builtin.c,v
retrieving revision 1.264
diff -p -u -r1.264 builtin.c
--- Src/builtin.c	7 Oct 2012 17:50:19 -0000	1.264
+++ Src/builtin.c	7 Oct 2012 19:04:12 -0000
@@ -548,8 +548,8 @@ bin_set(char *nam, char **args, UNUSED(O
     /* Obsolescent sh compatibility: set - is the same as set +xv *
      * and set - args is the same as set +xv -- args              */
     if (!EMULATION(EMULATE_ZSH) && *args && **args == '-' && !args[0][1]) {
-	dosetopt(VERBOSE, 0, 0);
-	dosetopt(XTRACE, 0, 0);
+	dosetopt(VERBOSE, 0, 0, opts);
+	dosetopt(XTRACE, 0, 0, opts);
 	if (!args[1])
 	    return 0;
     }
@@ -580,7 +580,7 @@ bin_set(char *nam, char **args, UNUSED(O
 		}
 		if(!(optno = optlookup(*args)))
 		    zerrnam(nam, "no such option: %s", *args);
-		else if(dosetopt(optno, action, 0))
+		else if(dosetopt(optno, action, 0, opts))
 		    zerrnam(nam, "can't change option: %s", *args);
 		break;
 	    } else if(**args == 'A') {
@@ -601,7 +601,7 @@ bin_set(char *nam, char **args, UNUSED(O
 	    else {
 	    	if (!(optno = optlookupc(**args)))
 		    zerrnam(nam, "bad option: -%c", **args);
-		else if(dosetopt(optno, action, 0))
+		else if(dosetopt(optno, action, 0, opts))
 		    zerrnam(nam, "can't change option: -%c", **args);
 	    }
 	}
@@ -5008,8 +5008,8 @@ bin_emulate(UNUSED(char *nam), char **ar
     int opt_L = OPT_ISSET(ops, 'L');
     int opt_R = OPT_ISSET(ops, 'R');
     int saveemulation, savesticky_emulation, savehackchar;
-    int ret = 1;
-    char saveopts[OPT_SIZE];
+    int ret = 1, new_emulation;
+    char saveopts[OPT_SIZE], new_opts[OPT_SIZE], savesticky_opts[OPT_SIZE];
     char *cmd = 0;
     const char *shname = *argv;
 
@@ -5044,7 +5044,7 @@ bin_emulate(UNUSED(char *nam), char **ar
 
     /* with single argument set current emulation */
     if (!argv[1]) {
-	emulate(shname, OPT_ISSET(ops,'R'));
+	emulate(shname, OPT_ISSET(ops,'R'), &emulation, opts);
 	if (OPT_ISSET(ops,'L'))
 	    opts[LOCALOPTIONS] = opts[LOCALTRAPS] = 1;
 	return 0;
@@ -5052,8 +5052,13 @@ bin_emulate(UNUSED(char *nam), char **ar
 
     argv++;
     memcpy(saveopts, opts, sizeof(opts));
+    memcpy(new_opts, opts, sizeof(opts));
     savehackchar = keyboardhackchar;
-    cmd = parseopts("emulate", &argv);
+    emulate(shname, OPT_ISSET(ops,'R'), &new_emulation, new_opts);
+    if (parseopts("emulate", &argv, new_opts, &cmd)) {
+	ret = 1;
+	goto restore;
+    }
 
     /* parseopts() has consumed anything that looks like an option */
     if (*argv) {
@@ -5061,6 +5066,9 @@ bin_emulate(UNUSED(char *nam), char **ar
 	goto restore;
     }
 
+    saveemulation = emulation;
+    emulation = new_emulation;
+    memcpy(opts, new_opts, sizeof(opts));
     /* If "-c command" is given, evaluate command using specified
      * emulation mode.
      */
@@ -5073,15 +5081,16 @@ bin_emulate(UNUSED(char *nam), char **ar
     } else
 	return 0;
 
-    saveemulation = emulation;
     savesticky_emulation = sticky_emulation;
-    emulate(shname, OPT_ISSET(ops,'R'));
     sticky_emulation = emulation;
+    memcpy(savesticky_opts, sticky_opts, sizeof(opts));
+    memcpy(sticky_opts, opts, sizeof(opts));
     ret = eval(argv);
     sticky_emulation = savesticky_emulation;
     emulation = saveemulation;
- restore:
     memcpy(opts, saveopts, sizeof(opts));
+    memcpy(sticky_opts, savesticky_opts, sizeof(opts));
+restore:
     keyboardhackchar = savehackchar;
     inittyptab();	/* restore banghist */
     return ret;
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.211
diff -p -u -r1.211 exec.c
--- Src/exec.c	21 Aug 2012 18:03:03 -0000	1.211
+++ Src/exec.c	7 Oct 2012 19:04:12 -0000
@@ -4544,7 +4544,7 @@ doshfunc(Shfunc shfunc, LinkList dosharg
 	 */
 	emulation = sticky_emulation = shfunc->emulation;
 	restore_sticky = 1;
-	installemulation();
+	installemulation(emulation, opts);
     } else
 	restore_sticky = 0;
 
Index: Src/init.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/init.c,v
retrieving revision 1.125
diff -p -u -r1.125 init.c
--- Src/init.c	5 Oct 2012 21:35:06 -0000	1.125
+++ Src/init.c	7 Oct 2012 19:04:12 -0000
@@ -246,7 +246,8 @@ parseargs(char **argv, char **runscript)
     opts[SHINSTDIN] = 0;
     opts[SINGLECOMMAND] = 0;
 
-    cmd = parseopts(NULL, &argv);
+    if (parseopts(NULL, &argv, opts, &cmd))
+	exit(1);
 
     paramlist = znewlinklist();
     if (*argv) {
@@ -276,18 +277,36 @@ parseargs(char **argv, char **runscript)
     argzero = ztrdup(argzero);
 }
 
+/*
+ * Parse shell options.
+ * If nam is not NULL, this is called from a command; don't
+ * exit on failure.
+ */
+
 /**/
-mod_export char *
-parseopts(char *nam, char ***argvp)
+mod_export int
+parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp)
 {
     int optionbreak = 0;
     int action, optno;
-    char *cmd = 0;	/* deliberately hides static */
     char **argv = *argvp;
 
-#define WARN_OPTION(F, S) if (nam) zwarnnam(nam, F, S); else zerr(F, S)
-#define LAST_OPTION(N) \
-	if (nam) { if (*argv) argv++; goto doneargv; } else exit(N)
+    *cmdp = 0;
+#define WARN_OPTION(F, S)						\
+    do {								\
+	if (nam)							\
+	    zwarnnam(nam, F, S);					\
+	else								\
+	    zerr(F, S);							\
+    } while (0)
+#define LAST_OPTION(N)	       \
+    do {		       \
+	if (nam) {	       \
+	    if (*argv)	       \
+		argv++;	       \
+	    goto doneargv;     \
+	} else exit(N);	       \
+    } while(0)
 
     /* loop through command line options (begins with "-" or "+") */
     while (!optionbreak && *argv && (**argv == '-' || **argv == '+')) {
@@ -327,25 +346,25 @@ parseopts(char *nam, char ***argvp)
 		optionbreak = 1;
 	    } else if (**argv == 'c') {
 		/* -c command */
-		cmd = *argv;
-		opts[INTERACTIVE] &= 1;
+		*cmdp = *argv;
+		new_opts[INTERACTIVE] &= 1;
 		scriptname = scriptfilename = ztrdup("zsh");
 	    } else if (**argv == 'o') {
 		if (!*++*argv)
 		    argv++;
 		if (!*argv) {
 		    WARN_OPTION("string expected after -o", NULL);
-		    LAST_OPTION(1);
+		    return 1;
 		}
 	    longoptions:
 		if (!(optno = optlookup(*argv))) {
 		    WARN_OPTION("no such option: %s", *argv);
-		    LAST_OPTION(1);
+		    return 1;
 		} else if (optno == RESTRICTED && !nam) {
 		    restricted = action;
 		} else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
 		    WARN_OPTION("can't change option: %s", *argv);
-		} else if (dosetopt(optno, action, !nam) && nam) {
+		} else if (dosetopt(optno, action, !nam, new_opts) && nam) {
 		    WARN_OPTION("can't change option: %s", *argv);
 		}
               break;
@@ -355,18 +374,18 @@ parseopts(char *nam, char ***argvp)
 		    if (!isspace(STOUC(**argv))) {
 		     badoptionstring:
 			WARN_OPTION("bad option string: '%s'", args);
-			LAST_OPTION(1);
+			return 1;
 		    }
 		break;
 	    } else {
 	    	if (!(optno = optlookupc(**argv))) {
 		    WARN_OPTION("bad option: -%c", **argv);
-		    LAST_OPTION(1);
+		    return 1;
 		} else if (optno == RESTRICTED && !nam) {
 		    restricted = action;
 		} else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
 		    WARN_OPTION("can't change option: %s", *argv);
-		} else if (dosetopt(optno, action, !nam) && nam) {
+		} else if (dosetopt(optno, action, !nam, new_opts) && nam) {
 		    WARN_OPTION("can't change option: -%c", **argv);
 		}
 	    }
@@ -374,18 +393,18 @@ parseopts(char *nam, char ***argvp)
 	argv++;
     }
  doneoptions:
-    if (cmd) {
+    if (*cmdp) {
 	if (!*argv) {
-	    WARN_OPTION("string expected after -%s", cmd);
-	    LAST_OPTION(1);
+	    WARN_OPTION("string expected after -%s", *cmdp);
+	    exit(1);
 	}
-	cmd = *argv++;
+	*cmdp = *argv++;
     }
  doneargv:
     *argvp = argv;
-    return cmd;
+    return 0;
 }
-    
+
 /**/
 static void
 printhelp(void)
@@ -1162,7 +1181,7 @@ init_misc(void)
 #else
     if (*zsh_name == 'r' || restricted)
 #endif
-	dosetopt(RESTRICTED, 1, 0);
+	dosetopt(RESTRICTED, 1, 0, opts);
     if (cmd) {
 	if (SHIN >= 10)
 	    fclose(bshin);
@@ -1225,7 +1244,7 @@ source(char *s)
     subsh  = 0;
     lineno = 1;
     loops  = 0;
-    dosetopt(SHINSTDIN, 0, 1);
+    dosetopt(SHINSTDIN, 0, 1, opts);
     scriptname = s;
     scriptfilename = s;
 
@@ -1297,7 +1316,7 @@ source(char *s)
     thisjob = cj;                    /* current job number                   */
     lineno = oldlineno;              /* our current lineno                   */
     loops = oloops;                  /* the # of nested loops we are in      */
-    dosetopt(SHINSTDIN, oldshst, 1); /* SHINSTDIN option                     */
+    dosetopt(SHINSTDIN, oldshst, 1, opts); /* SHINSTDIN option               */
     errflag = 0;
     if (!exit_pending)
 	retflag = 0;
@@ -1535,7 +1554,7 @@ zsh_main(UNUSED(int argc), char **argv)
     fdtable = zshcalloc(fdtable_size*sizeof(*fdtable));
 
     createoptiontable();
-    emulate(zsh_name, 1);   /* initialises most options */
+    emulate(zsh_name, 1, &emulation, opts);   /* initialises most options */
     opts[LOGINSHELL] = (**argv == '-');
     opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
     opts[USEZLE] = 1;   /* may be unset in init_io() */
Index: Src/options.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/options.c,v
retrieving revision 1.63
diff -p -u -r1.63 options.c
--- Src/options.c	5 Oct 2012 21:43:00 -0000	1.63
+++ Src/options.c	7 Oct 2012 19:04:12 -0000
@@ -44,6 +44,14 @@ mod_export int sticky_emulation;
  
 /**/
 mod_export char opts[OPT_SIZE];
+
+/*
+ * the options that need setting for current sticky emulation, if any:
+ * same format as opts.
+ */
+
+/**/
+mod_export char sticky_opts[OPT_SIZE];
  
 /* Option name hash table */
 
@@ -70,7 +78,7 @@ mod_export HashTable optiontab;
 /* option is an alias to an other option */
 #define OPT_ALIAS	(EMULATE_UNUSED<<2)
 
-#define defset(X) (!!((X)->node.flags & emulation))
+#define defset(X, my_emulation) (!!((X)->node.flags & my_emulation))
 
 /*
  * Note that option names should usually be fewer than 20 characters long
@@ -439,11 +447,11 @@ printoptionnode(HashNode hn, int set)
     if (optno < 0)
 	optno = -optno;
     if (isset(KSHOPTIONPRINT)) {
-	if (defset(on))
+	if (defset(on, emulation))
 	    printf("no%-19s %s\n", on->node.nam, isset(optno) ? "off" : "on");
 	else
 	    printf("%-21s %s\n", on->node.nam, isset(optno) ? "on" : "off");
-    } else if (set == (isset(optno) ^ defset(on))) {
+    } else if (set == (isset(optno) ^ defset(on, emulation))) {
 	if (set ^ isset(optno))
 	    fputs("no", stdout);
 	puts(on->node.nam);
@@ -475,6 +483,15 @@ createoptiontable(void)
 	optiontab->addnode(optiontab, on->node.nam, on);
 }
 
+/* Emulation appropriate to the setemulate function */
+
+static int setemulate_emulation;
+
+/* Option array manipulated within the setemulate function */
+
+/**/
+static char *setemulate_opts;
+
 /* Setting of default options */
 
 /**/
@@ -490,20 +507,22 @@ setemulate(HashNode hn, int fully)
     if (!(on->node.flags & OPT_ALIAS) &&
 	((fully && !(on->node.flags & OPT_SPECIAL)) ||
 	 (on->node.flags & OPT_EMULATE)))
-	opts[on->optno] = defset(on);
+	setemulate_opts[on->optno] = defset(on, setemulate_emulation);
 }
 
 /**/
 void
-installemulation(void)
+installemulation(int new_emulation, char *new_opts)
 {
+    setemulate_emulation = new_emulation;
+    setemulate_opts = new_opts;
     scanhashtable(optiontab, 0, 0, 0, setemulate,
-		  !!(emulation & EMULATE_FULLY));
+		  !!(new_emulation & EMULATE_FULLY));
 }
 
 /**/
 void
-emulate(const char *zsh_name, int fully)
+emulate(const char *zsh_name, int fully, int *new_emulation, char *new_opts)
 {
     char ch = *zsh_name;
 
@@ -512,17 +531,17 @@ emulate(const char *zsh_name, int fully)
 
     /* Work out the new emulation mode */
     if (ch == 'c')
-	emulation = EMULATE_CSH;
+	*new_emulation = EMULATE_CSH;
     else if (ch == 'k')
-	emulation = EMULATE_KSH;
+	*new_emulation = EMULATE_KSH;
     else if (ch == 's' || ch == 'b')
-	emulation = EMULATE_SH;
+	*new_emulation = EMULATE_SH;
     else
-	emulation = EMULATE_ZSH;
+	*new_emulation = EMULATE_ZSH;
 
     if (fully)
-	emulation |= EMULATE_FULLY;
-    installemulation();
+	*new_emulation |= EMULATE_FULLY;
+    installemulation(*new_emulation, new_opts);
 
     if (funcstack && funcstack->tp == FS_FUNC) {
 	/*
@@ -534,7 +553,7 @@ emulate(const char *zsh_name, int fully)
 	Shfunc shf = (Shfunc)shfunctab->getnode(shfunctab, funcstack->name);
 	if (shf && (shf->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL))) {
 	    /* Tracing is on, so set xtrace */
-	    opts[XTRACE] = 1;
+	    new_opts[XTRACE] = 1;
 	}
     }
 }
@@ -545,7 +564,7 @@ emulate(const char *zsh_name, int fully)
 static void
 setoption(HashNode hn, int value)
 {
-    dosetopt(((Optname) hn)->optno, value, 0);
+    dosetopt(((Optname) hn)->optno, value, 0, opts);
 }
 
 /**/
@@ -582,7 +601,7 @@ bin_setopt(char *nam, char **args, UNUSE
 		}
 		if(!(optno = optlookup(*args)))
 		    zwarnnam(nam, "no such option: %s", *args);
-		else if(dosetopt(optno, action, 0))
+		else if(dosetopt(optno, action, 0, opts))
 		    zwarnnam(nam, "can't change option: %s", *args);
 		break;
 	    } else if(**args == 'm') {
@@ -590,7 +609,7 @@ bin_setopt(char *nam, char **args, UNUSE
 	    } else {
 	    	if (!(optno = optlookupc(**args)))
 		    zwarnnam(nam, "bad option: -%c", **args);
-		else if(dosetopt(optno, action, 0))
+		else if(dosetopt(optno, action, 0, opts))
 		    zwarnnam(nam, "can't change option: -%c", **args);
 	    }
 	}
@@ -603,7 +622,7 @@ bin_setopt(char *nam, char **args, UNUSE
 	while (*args) {
 	    if(!(optno = optlookup(*args++)))
 		zwarnnam(nam, "no such option: %s", args[-1]);
-	    else if(dosetopt(optno, !isun, 0))
+	    else if(dosetopt(optno, !isun, 0, opts))
 		zwarnnam(nam, "can't change option: %s", args[-1]);
 	}
     } else {
@@ -713,7 +732,7 @@ static char *rparams[] = {
 
 /**/
 mod_export int
-dosetopt(int optno, int value, int force)
+dosetopt(int optno, int value, int force, char *new_opts)
 {
     if(!optno)
 	return -1;
@@ -735,7 +754,7 @@ dosetopt(int optno, int value, int force
 	return -1;
     } else if(!force && (optno == INTERACTIVE || optno == SHINSTDIN ||
 	    optno == SINGLECOMMAND)) {
-	if (opts[optno] == value)
+	if (new_opts[optno] == value)
 	    return 0;
 	/* it is not permitted to change the value of these options */
 	return -1;
@@ -751,7 +770,7 @@ dosetopt(int optno, int value, int force
 #endif /* HAVE_SETUID */
 #ifdef JOB_CONTROL
     } else if (!force && optno == MONITOR && value) {
-	if (opts[optno] == value)
+	if (new_opts[optno] == value)
 	    return 0;
 	if (SHTTY != -1) {
 	    origpgrp = GETPGRP();
@@ -770,12 +789,12 @@ dosetopt(int optno, int value, int force
 	if (sticky_emulation)
 	    return -1;
 	zleentry(ZLE_CMD_SET_KEYMAP, optno);
-	opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0;
+	new_opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0;
     } else if (optno == SUNKEYBOARDHACK) {
 	/* for backward compatibility */
 	keyboardhackchar = (value ? '`' : '\0');
     }
-    opts[optno] = value;
+    new_opts[optno] = value;
     if (optno == BANGHIST || optno == SHINSTDIN)
 	inittyptab();
     return 0;
@@ -817,10 +836,11 @@ printoptionnodestate(HashNode hn, int ha
     int optno = on->optno;
 
     if (hadplus) {
-        if (defset(on) != isset(optno))
-	    printf("set -o %s%s\n", defset(on) ? "no" : "", on->node.nam);
+        if (defset(on, emulation) != isset(optno))
+	    printf("set -o %s%s\n", defset(on, emulation) ?
+		   "no" : "", on->node.nam);
     } else {
-	if (defset(on))
+	if (defset(on, emulation))
 	    printf("no%-19s %s\n", on->node.nam, isset(optno) ? "off" : "on");
 	else
 	    printf("%-21s %s\n", on->node.nam, isset(optno) ? "on" : "off");
Index: Src/Modules/parameter.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/parameter.c,v
retrieving revision 1.54
diff -p -u -r1.54 parameter.c
--- Src/Modules/parameter.c	5 Mar 2012 10:06:29 -0000	1.54
+++ Src/Modules/parameter.c	7 Oct 2012 19:04:12 -0000
@@ -771,7 +771,7 @@ setpmoption(Param pm, char *value)
 	zwarn("invalid value: %s", value);
     else if (!(n = optlookup(pm->node.nam)))
 	zwarn("no such option: %s", pm->node.nam);
-    else if (dosetopt(n, (value && strcmp(value, "off")), 0))
+    else if (dosetopt(n, (value && strcmp(value, "off")), 0, opts))
 	zwarn("can't change option: %s", pm->node.nam);
     zsfree(value);
 }
@@ -784,7 +784,7 @@ unsetpmoption(Param pm, UNUSED(int exp))
 
     if (!(n = optlookup(pm->node.nam)))
 	zwarn("no such option: %s", pm->node.nam);
-    else if (dosetopt(n, 0, 0))
+    else if (dosetopt(n, 0, 0, opts))
 	zwarn("can't change option: %s", pm->node.nam);
 }
 
@@ -812,7 +812,7 @@ setpmoptions(UNUSED(Param pm), HashTable
 	    if (!val || (strcmp(val, "on") && strcmp(val, "off")))
 		zwarn("invalid value: %s", val);
 	    else if (dosetopt(optlookup(hn->nam),
-			      (val && strcmp(val, "off")), 0))
+			      (val && strcmp(val, "off")), 0, opts))
 		zwarn("can't change option: %s", hn->nam);
 	}
     deleteparamtable(ht);
Index: Test/B07emulate.ztst
===================================================================
RCS file: /cvsroot/zsh/zsh/Test/B07emulate.ztst,v
retrieving revision 1.2
diff -p -u -r1.2 B07emulate.ztst
--- Test/B07emulate.ztst	11 Feb 2009 20:42:17 -0000	1.2
+++ Test/B07emulate.ztst	7 Oct 2012 19:04:12 -0000
@@ -176,3 +176,28 @@
 0:Sticky emulation not triggered if sticky emulation unchanged
 >on
 >off
+
+  (
+  setopt ignorebraces
+  emulate zsh -o extendedglob -c '
+  [[ -o ignorebraces ]] || print "Yay, ignorebraces was reset"
+  [[ -o extendedglob ]] && print "Yay, extendedglob is set"
+  '
+  )
+0:emulate -c with options
+>Yay, ignorebraces was reset
+>Yay, extendedglob is set
+
+  (
+  setopt ignorebraces          
+  emulate zsh -o extendedglob
+  [[ -o ignorebraces ]] || print "Yay, ignorebraces is no longer set"
+  [[ -o extendedglob ]] && print "Yay, extendedglob is set"
+  )
+0:emulate with options but no -c
+>Yay, ignorebraces is no longer set
+>Yay, extendedglob is set
+
+  emulate zsh -o fixallmybugs 'print This was executed, bad'
+1:emulate -c with incorrect options
+?(eval):emulate:1: no such option: fixallmybugs


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

* Re: emulate with options other than -c is broken
  2012-10-07 19:26 ` Peter Stephenson
@ 2012-10-11 19:53   ` Peter Stephenson
  2012-10-13  4:01     ` Bart Schaefer
  0 siblings, 1 reply; 4+ messages in thread
From: Peter Stephenson @ 2012-10-11 19:53 UTC (permalink / raw)
  To: Zsh Hackers' List

On Sun, 7 Oct 2012 20:26:06 +0100
Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> There's one thing still only half fixed:
> 
> emulate zsh -o extendedglob -c 'func() { [[ -o extendedglob ]] &&
>   print is on }'
> func
> 
> doesn't print "is on" yet.  This sure as heck looks like a bug: if you
> get to pass options uniformly with the emulation, why would the
> emulation but not the options be sticky?  (It's half fixed because
> I've created and initialised a sticky_opts array but not used it.)

I suppose it's not suprising this turned into either a can of worms, or
maybe a dog's dinner, or a pig's breakfast.

My idea of simply recording all options didn't work:  you need to know
not just the options that are in effect, but which options need
changing.  Given that typically you're not going to give that many
options in addition to the emulation, it seemed preferable to record
only the options that explicitly needed turning off or turning on.

The other flea in the dog's wormy breakfast was that in that case, if
you want to be able to compare two different emulations with additional
options simply, you need to ensure the options are recorded in order.

The next question is, do we want to use this for functions autoloaded by
the completion system?  The argument against is that it changes the
behaviour (we'd probably need to enter zsh emulation plus set a few
options, rather than just set explicit options --- but note the property
that if the emulation is the same we do allow option changes to
propagate between functions, as at present), and also we need more
memory to store the sticky emulation for every function.  The argument in
favour is the behaviour is easier to document because it's based on
emulations, and the function behaviour is guaranteed wherever it's
loaded or called, not just from somewhere under _main_complete (remember
what got me into this was the fact that forcing _path_files to autoload
outside the completion system gave an error).

Index: Doc/Zsh/builtins.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/builtins.yo,v
retrieving revision 1.144
diff -p -u -r1.144 builtins.yo
--- Doc/Zsh/builtins.yo	7 Oct 2012 17:50:19 -0000	1.144
+++ Doc/Zsh/builtins.yo	11 Oct 2012 19:43:39 -0000
@@ -462,6 +462,10 @@ sitem(4.)(The presence or absence of the
 corresponds to different sticky emulation modes, so for example
 `tt(emulate sh -c)', `tt(emulate -R sh -c)' and `tt(emulate csh -c)'
 are treated as three distinct sticky emulations.)
+sitem(5.)(Difference in shell options supplied in addition to the
+basic emulation also mean the sticky emulations are different, so for
+example `tt(emulate zsh -c)' and `tt(emulate zsh -o cbases -c)' are
+treated as distinct sticky emulations.)
 endsitem()
 )
 findex(enable)
Index: Src/builtin.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/builtin.c,v
retrieving revision 1.266
diff -p -u -r1.266 builtin.c
--- Src/builtin.c	9 Oct 2012 09:24:23 -0000	1.266
+++ Src/builtin.c	11 Oct 2012 19:43:39 -0000
@@ -2944,7 +2944,7 @@ bin_functions(char *name, char **argv, O
 	    shf = (Shfunc) zshcalloc(sizeof *shf);
 	    shf->node.flags = on;
 	    shf->funcdef = mkautofn(shf);
-	    shf->emulation = sticky_emulation;
+	    shfunc_set_sticky(shf);
 	    shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
 
 	    if (signum != -1) {
@@ -5007,11 +5007,15 @@ bin_emulate(UNUSED(char *nam), char **ar
 {
     int opt_L = OPT_ISSET(ops, 'L');
     int opt_R = OPT_ISSET(ops, 'R');
-    int saveemulation, savesticky_emulation, savehackchar;
+    int saveemulation, savehackchar;
     int ret = 1, new_emulation;
-    char saveopts[OPT_SIZE], new_opts[OPT_SIZE], savesticky_opts[OPT_SIZE];
+    char saveopts[OPT_SIZE], new_opts[OPT_SIZE];
     char *cmd = 0;
     const char *shname = *argv;
+    LinkList optlist;
+    LinkNode optnode;
+    Emulation_options save_sticky;
+    OptIndex *on_ptr, *off_ptr;
 
     /* without arguments just print current emulation */
     if (!shname) {
@@ -5055,7 +5059,8 @@ bin_emulate(UNUSED(char *nam), char **ar
     memcpy(new_opts, opts, sizeof(opts));
     savehackchar = keyboardhackchar;
     emulate(shname, OPT_ISSET(ops,'R'), &new_emulation, new_opts);
-    if (parseopts("emulate", &argv, new_opts, &cmd)) {
+    optlist = newlinklist();
+    if (parseopts("emulate", &argv, new_opts, &cmd, optlist)) {
 	ret = 1;
 	goto restore;
     }
@@ -5081,15 +5086,40 @@ bin_emulate(UNUSED(char *nam), char **ar
     } else
 	return 0;
 
-    savesticky_emulation = sticky_emulation;
-    sticky_emulation = emulation;
-    memcpy(savesticky_opts, sticky_opts, sizeof(opts));
-    memcpy(sticky_opts, opts, sizeof(opts));
+    save_sticky = sticky;
+    sticky = hcalloc(sizeof(*sticky));
+    sticky->emulation = emulation;
+    for (optnode = firstnode(optlist); optnode; incnode(optnode)) {
+	/* Data is index into new_opts */
+	char *optptr = (char *)getdata(optnode);
+	if (*optptr)
+	    sticky->n_on_opts++;
+	else
+	    sticky->n_off_opts++;
+    }
+    if (sticky->n_on_opts)
+	on_ptr = sticky->on_opts =
+	    zhalloc(sticky->n_on_opts * sizeof(*sticky->on_opts));
+    else
+	on_ptr = NULL;
+    if (sticky->n_off_opts)
+	off_ptr = sticky->off_opts = zhalloc(sticky->n_off_opts *
+					     sizeof(*sticky->off_opts));
+    else
+	off_ptr = NULL;
+    for (optnode = firstnode(optlist); optnode; incnode(optnode)) {
+	/* Data is index into new_opts */
+	char *optptr = (char *)getdata(optnode);
+	int optno = optptr - new_opts;
+	if (*optptr)
+	    *on_ptr++ = optno;
+	else
+	    *off_ptr++ = optno;
+    }
     ret = eval(argv);
-    sticky_emulation = savesticky_emulation;
+    sticky = save_sticky;
     emulation = saveemulation;
     memcpy(opts, saveopts, sizeof(opts));
-    memcpy(sticky_opts, savesticky_opts, sizeof(opts));
 restore:
     keyboardhackchar = savehackchar;
     inittyptab();	/* restore banghist */
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.213
diff -p -u -r1.213 exec.c
--- Src/exec.c	11 Oct 2012 16:36:14 -0000	1.213
+++ Src/exec.c	11 Oct 2012 19:43:39 -0000
@@ -4267,7 +4267,7 @@ execfuncdef(Estate state, UNUSED(int do_
 	shf->node.flags = 0;
 	shf->filename = ztrdup(scriptfilename);
 	shf->lineno = lineno;
-	shf->emulation = sticky_emulation;
+	shfunc_set_sticky(shf);
 
 	if (!names) {
 	    /*
@@ -4319,6 +4319,46 @@ execfuncdef(Estate state, UNUSED(int do_
     return ret;
 }
 
+/* Duplicate a sticky emulation */
+
+/**/
+
+mod_export Emulation_options
+sticky_emulation_dup(Emulation_options src, int useheap)
+{
+    Emulation_options newsticky = useheap ?
+	hcalloc(sizeof(*src)) : zshcalloc(sizeof(*src));
+    newsticky->emulation = src->emulation;
+    if (src->n_on_opts) {
+	size_t sz = src->n_on_opts * sizeof(*src->on_opts);
+	newsticky->n_on_opts = src->n_on_opts;
+	newsticky->on_opts = useheap ? zhalloc(sz) : zalloc(sz);
+	memcpy(newsticky->on_opts, src->on_opts, sz);
+    }
+    if (src->n_off_opts) {
+	size_t sz = src->n_off_opts * sizeof(*src->off_opts);
+	newsticky->n_off_opts = src->n_off_opts;
+	newsticky->off_opts = useheap ? zhalloc(sz) : zalloc(sz);
+	memcpy(newsticky->off_opts, src->off_opts, sz);
+    }
+
+    return newsticky;
+}
+
+/* Set the sticky emulation attributes for a shell function */
+
+/**/
+
+mod_export void
+shfunc_set_sticky(Shfunc shf)
+{
+    if (sticky)
+	shf->sticky = sticky_emulation_dup(sticky, 0);
+    else
+	shf->sticky = NULL;
+}
+
+
 /* Main entry point to execute a shell function. */
 
 /**/
@@ -4479,6 +4519,45 @@ loadautofn(Shfunc shf, int fksh, int aut
 }
 
 /*
+ * Check if a sticky emulation differs from the current one.
+ */
+
+/**/
+
+int sticky_emulation_differs(Emulation_options sticky2)
+{
+    /* If no new sticky emulation, not a different emulation */
+    if (!sticky2)
+	return 0;
+    /* If no current sticky emulation, different */
+    if (!sticky)
+	return 1;
+    /* If basic emulation different, different */
+    if (sticky->emulation != sticky2->emulation)
+	return 1;
+    /* If differing numbers of options, different */
+    if (sticky->n_on_opts != sticky2->n_on_opts ||
+	sticky->n_off_opts != sticky2->n_off_opts)
+	return 1;
+    /*
+     * We need to compare option arrays, if non-null.
+     * We made parseopts() create the list of options in option
+     * order to make this easy.
+     */
+    /* If different options turned on, different */
+    if (sticky->n_on_opts &&
+	memcmp(sticky->on_opts, sticky2->on_opts,
+	       sticky->n_on_opts * sizeof(*sticky->on_opts)) != 0)
+	return 1;
+    /* If different options turned on, different */
+    if (sticky->n_off_opts &&
+	memcmp(sticky->off_opts, sticky2->off_opts,
+	       sticky->n_off_opts * sizeof(*sticky->off_opts)) != 0)
+	return 1;
+    return 0;
+}
+
+/*
  * execute a shell function
  *
  * name is the name of the function
@@ -4507,10 +4586,11 @@ doshfunc(Shfunc shfunc, LinkList dosharg
     char *name = shfunc->node.nam;
     int flags = shfunc->node.flags, ooflags;
     char *fname = dupstring(name);
-    int obreaks, saveemulation, savesticky_emulation, restore_sticky;
+    int obreaks, saveemulation, restore_sticky;
     Eprog prog;
     struct funcstack fstack;
     static int oflags;
+    Emulation_options save_sticky = NULL;
 #ifdef MAX_FUNCTION_DEPTH
     static int funcdepth;
 #endif
@@ -4548,9 +4628,9 @@ doshfunc(Shfunc shfunc, LinkList dosharg
      * function we need to restore the original options on exit.   */
     memcpy(saveopts, opts, sizeof(opts));
     saveemulation = emulation;
-    savesticky_emulation = sticky_emulation;
+    save_sticky = sticky;
 
-    if (shfunc->emulation && sticky_emulation != shfunc->emulation) {
+    if (sticky_emulation_differs(shfunc->sticky)) {
 	/*
 	 * Function is marked for sticky emulation.
 	 * Enable it now.
@@ -4563,9 +4643,24 @@ doshfunc(Shfunc shfunc, LinkList dosharg
 	 *
 	 * This propagates the sticky emulation to subfunctions.
 	 */
-	emulation = sticky_emulation = shfunc->emulation;
+	sticky = sticky_emulation_dup(shfunc->sticky, 1);
+	emulation = sticky->emulation;
 	restore_sticky = 1;
 	installemulation(emulation, opts);
+	if (sticky->n_on_opts) {
+	    OptIndex *onptr;
+	    for (onptr = sticky->on_opts;
+		 onptr < sticky->on_opts + sticky->n_on_opts;
+		 onptr++)
+		opts[*onptr] = 1;
+	}
+	if (sticky->n_off_opts) {
+	    OptIndex *offptr;
+	    for (offptr = sticky->off_opts;
+		 offptr < sticky->off_opts + sticky->n_off_opts;
+		 offptr++)
+		opts[*offptr] = 0;
+	}
     } else
 	restore_sticky = 0;
 
@@ -4674,7 +4769,7 @@ doshfunc(Shfunc shfunc, LinkList dosharg
 	 */
 	memcpy(opts, saveopts, sizeof(opts));
 	emulation = saveemulation;
-	sticky_emulation = savesticky_emulation;
+	sticky = save_sticky;
     } else if (isset(LOCALOPTIONS)) {
 	/* restore all shell options except PRIVILEGED and RESTRICTED */
 	saveopts[PRIVILEGED] = opts[PRIVILEGED];
Index: Src/hashtable.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/hashtable.c,v
retrieving revision 1.37
diff -p -u -r1.37 hashtable.c
--- Src/hashtable.c	21 Aug 2012 18:03:03 -0000	1.37
+++ Src/hashtable.c	11 Oct 2012 19:43:39 -0000
@@ -888,6 +888,15 @@ freeshfuncnode(HashNode hn)
     if (shf->funcdef)
 	freeeprog(shf->funcdef);
     zsfree(shf->filename);
+    if (shf->sticky) {
+	if (shf->sticky->n_on_opts)
+	    zfree(shf->sticky->on_opts,
+		  shf->sticky->n_on_opts * sizeof(*shf->sticky->on_opts));
+	if (shf->sticky->n_off_opts)
+	    zfree(shf->sticky->off_opts,
+		  shf->sticky->n_off_opts * sizeof(*shf->sticky->off_opts));
+	zfree(shf->sticky, sizeof(*shf->sticky));
+    }
     zfree(shf, sizeof(struct shfunc));
 }
 
Index: Src/init.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/init.c,v
retrieving revision 1.126
diff -p -u -r1.126 init.c
--- Src/init.c	7 Oct 2012 19:46:46 -0000	1.126
+++ Src/init.c	11 Oct 2012 19:43:39 -0000
@@ -246,7 +246,7 @@ parseargs(char **argv, char **runscript)
     opts[SHINSTDIN] = 0;
     opts[SINGLECOMMAND] = 0;
 
-    if (parseopts(NULL, &argv, opts, &cmd))
+    if (parseopts(NULL, &argv, opts, &cmd, NULL))
 	exit(1);
 
     paramlist = znewlinklist();
@@ -277,15 +277,37 @@ parseargs(char **argv, char **runscript)
     argzero = ztrdup(argzero);
 }
 
+/* Insert into list in order of pointer value */
+
+/**/
+static void
+parseopts_insert(LinkList optlist, void *ptr)
+{
+    LinkNode node;
+
+    for (node = firstnode(optlist); node; incnode(node)) {
+	if (ptr < getdata(node)) {
+	    insertlinknode(optlist, prevnode(node), ptr);
+	    return;
+	}
+    }
+
+    addlinknode(optlist, ptr);
+}
+
 /*
  * Parse shell options.
  * If nam is not NULL, this is called from a command; 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.
  */
 
 /**/
 mod_export int
-parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp)
+parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
+	  LinkList optlist)
 {
     int optionbreak = 0;
     int action, optno;
@@ -364,8 +386,12 @@ parseopts(char *nam, char ***argvp, char
 		    restricted = action;
 		} else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
 		    WARN_OPTION("can't change option: %s", *argv);
-		} else if (dosetopt(optno, action, !nam, new_opts) && nam) {
-		    WARN_OPTION("can't change option: %s", *argv);
+		} else {
+		    if (dosetopt(optno, action, !nam, new_opts) && nam) {
+			WARN_OPTION("can't change option: %s", *argv);
+		    } else if (optlist) {
+			parseopts_insert(optlist, new_opts+optno);
+		    }
 		}
               break;
 	    } else if (isspace(STOUC(**argv))) {
@@ -385,8 +411,12 @@ parseopts(char *nam, char ***argvp, char
 		    restricted = action;
 		} else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
 		    WARN_OPTION("can't change option: %s", *argv);
-		} else if (dosetopt(optno, action, !nam, new_opts) && nam) {
-		    WARN_OPTION("can't change option: -%c", **argv);
+		} else {
+		    if (dosetopt(optno, action, !nam, new_opts) && nam) {
+			WARN_OPTION("can't change option: -%c", **argv);
+		    } else if (optlist) {
+			parseopts_insert(optlist, new_opts+optno);
+		    }
 		}
 	    }
 	}
@@ -396,7 +426,7 @@ parseopts(char *nam, char ***argvp, char
     if (*cmdp) {
 	if (!*argv) {
 	    WARN_OPTION("string expected after -%s", *cmdp);
-	    exit(1);
+	    return 1;
 	}
 	*cmdp = *argv++;
     }
Index: Src/options.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/options.c,v
retrieving revision 1.64
diff -p -u -r1.64 options.c
--- Src/options.c	7 Oct 2012 19:46:46 -0000	1.64
+++ Src/options.c	11 Oct 2012 19:43:39 -0000
@@ -35,29 +35,21 @@
 /**/
 mod_export int emulation;
  
-/* current sticky emulation:  0 means none */
+/* current sticky emulation:  sticky = NULL means none */
 
 /**/
-mod_export int sticky_emulation;
+mod_export Emulation_options sticky;
 
 /* the options; e.g. if opts[SHGLOB] != 0, SH_GLOB is turned on */
- 
+
 /**/
 mod_export char opts[OPT_SIZE];
 
-/*
- * the options that need setting for current sticky emulation, if any:
- * same format as opts.
- */
-
-/**/
-mod_export char sticky_opts[OPT_SIZE];
- 
 /* Option name hash table */
 
 /**/
 mod_export HashTable optiontab;
- 
+
 /* The canonical option name table */
 
 #define OPT_CSH		EMULATE_CSH
@@ -786,7 +778,7 @@ dosetopt(int optno, int value, int force
 	    return -1;
 #endif /* GETPWNAM_FAKED */
     } else if ((optno == EMACSMODE || optno == VIMODE) && value) {
-	if (sticky_emulation)
+	if (sticky && sticky->emulation)
 	    return -1;
 	zleentry(ZLE_CMD_SET_KEYMAP, optno);
 	new_opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0;
Index: Src/parse.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/parse.c,v
retrieving revision 1.91
diff -p -u -r1.91 parse.c
--- Src/parse.c	5 Oct 2012 21:35:06 -0000	1.91
+++ Src/parse.c	11 Oct 2012 19:43:40 -0000
@@ -3482,7 +3482,7 @@ dump_autoload(char *nam, char *file, int
 	shf = (Shfunc) zshcalloc(sizeof *shf);
 	shf->node.flags = on;
 	shf->funcdef = mkautofn(shf);
-	shf->emulation = 0;
+	shf->sticky = NULL;
 	shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf);
 	if (OPT_ISSET(ops,'X') && eval_autoload(shf, shf->node.nam, ops, func))
 	    ret = 1;
Index: Src/signals.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/signals.c,v
retrieving revision 1.65
diff -p -u -r1.65 signals.c
--- Src/signals.c	25 Sep 2011 18:09:14 -0000	1.65
+++ Src/signals.c	11 Oct 2012 19:43:40 -0000
@@ -755,7 +755,10 @@ dosavetrap(int sig, int level)
 	    newshf->node.flags = shf->node.flags;
 	    newshf->funcdef = dupeprog(shf->funcdef, 0);
 	    newshf->filename = ztrdup(shf->filename);
-	    newshf->emulation = shf->emulation;
+	    if (shf->sticky) {
+		newshf->sticky = sticky_emulation_dup(shf->sticky, 0);
+	    } else
+		newshf->sticky = 0;
 	    if (shf->node.flags & PM_UNDEFINED)
 		newshf->funcdef->shf = newshf;
 	}
Index: Src/zsh.h
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v
retrieving revision 1.185
diff -p -u -r1.185 zsh.h
--- Src/zsh.h	5 Oct 2012 21:35:06 -0000	1.185
+++ Src/zsh.h	11 Oct 2012 19:43:41 -0000
@@ -407,6 +407,7 @@ typedef struct cmdnam    *Cmdnam;
 typedef struct complist  *Complist;
 typedef struct conddef   *Conddef;
 typedef struct dirsav    *Dirsav;
+typedef struct emulation_options *Emulation_options;
 typedef struct features  *Features;
 typedef struct feature_enables  *Feature_enables;
 typedef struct funcstack *Funcstack;
@@ -1099,7 +1100,7 @@ struct shfunc {
     char *filename;             /* Name of file located in */
     zlong lineno;		/* line number in above file */
     Eprog funcdef;		/* function definition    */
-    int emulation;		/* sticky emulation for function */
+    Emulation_options sticky;   /* sticky emulation definitions, if any */
 };
 
 /* Shell function context types. */
@@ -2104,6 +2105,12 @@ enum {
     OPT_SIZE
 };
 
+/*
+ * Size required to fit an option number.
+ * If OPT_SIZE goes above 256 this will need to expand.
+ */
+typedef unsigned char OptIndex;
+
 #undef isset
 #define isset(X) (opts[X])
 #define unset(X) (!opts[X])
@@ -2112,6 +2119,27 @@ enum {
 #define jobbing  (isset(MONITOR))
 #define islogin  (isset(LOGINSHELL))
 
+/*
+ * Record of emulation and options that need to be set
+ * for a full "emulate".
+ */
+struct emulation_options {
+    /* The emulation itself */
+    int emulation;
+    /* The number of options in on_opts. */
+    int n_on_opts;
+    /* The number of options in off_opts. */
+    int n_off_opts;
+    /*
+     * Array of options to be turned on.
+     * Only options specified explicitly in the emulate command
+     * are recorded.  Null if n_on_opts is zero.
+     */
+    OptIndex *on_opts;
+    /* Array of options to be turned off, similar. */
+    OptIndex *off_opts;
+};
+
 /***********************************************/
 /* Definitions for terminal and display control */
 /***********************************************/
Index: Src/Modules/parameter.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/parameter.c,v
retrieving revision 1.55
diff -p -u -r1.55 parameter.c
--- Src/Modules/parameter.c	7 Oct 2012 19:46:46 -0000	1.55
+++ Src/Modules/parameter.c	11 Oct 2012 19:43:41 -0000
@@ -289,7 +289,7 @@ setfunction(char *name, char *val, int d
     shf = (Shfunc) zshcalloc(sizeof(*shf));
     shf->funcdef = dupeprog(prog, 0);
     shf->node.flags = dis;
-    shf->emulation = sticky_emulation;
+    shfunc_set_sticky(shf);
 
     if (!strncmp(name, "TRAP", 4) &&
 	(sn = getsignum(name + 4)) != -1) {
Index: Test/B07emulate.ztst
===================================================================
RCS file: /cvsroot/zsh/zsh/Test/B07emulate.ztst,v
retrieving revision 1.3
diff -p -u -r1.3 B07emulate.ztst
--- Test/B07emulate.ztst	7 Oct 2012 19:46:46 -0000	1.3
+++ Test/B07emulate.ztst	11 Oct 2012 19:43:41 -0000
@@ -201,3 +201,49 @@
   emulate zsh -o fixallmybugs 'print This was executed, bad'
 1:emulate -c with incorrect options
 ?(eval):emulate:1: no such option: fixallmybugs
+
+  emulate zsh -c '
+    func() { [[ -o extendedglob ]] || print extendedglob is off }
+  '
+  func
+  emulate zsh -o extendedglob -c '
+    func() { [[ -o extendedglob ]] && print extendedglob is on }
+  '
+  func
+0:options specified alongside emulation are also sticky
+>extendedglob is off
+>extendedglob is on
+
+  emulate zsh -o extendedglob -c '
+    func_inner() { setopt nobareglobqual }
+  '
+  emulate zsh -o extendedglob -c '
+    func_outer() { 
+      func_inner
+      [[ -o bareglobqual ]] || print bareglobqual was turned off
+      [[ -o extendedglob ]] && print extendedglob is on, though
+    }
+  '
+  [[ -o extendedglob ]] || print extendedglob is initially off
+  func_outer
+0:options propagate between identical emulations
+>extendedglob is initially off
+>bareglobqual was turned off
+>extendedglob is on, though
+
+  emulate zsh -o extendedglob -c '
+    func_inner() { setopt nobareglobqual }
+  '
+  emulate zsh -o extendedglob -o cbases -c '
+    func_outer() { 
+      func_inner
+      [[ -o bareglobqual ]] && print bareglobqual is still on
+      [[ -o extendedglob ]] && print extendedglob is on, too
+    }
+  '
+  [[ -o extendedglob ]] || print extendedglob is initially off
+  func_outer
+0:options do not propagate between different emulations
+>extendedglob is initially off
+>bareglobqual is still on
+>extendedglob is on, too

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


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

* Re: emulate with options other than -c is broken
  2012-10-11 19:53   ` Peter Stephenson
@ 2012-10-13  4:01     ` Bart Schaefer
  0 siblings, 0 replies; 4+ messages in thread
From: Bart Schaefer @ 2012-10-13  4:01 UTC (permalink / raw)
  To: Zsh Hackers' List

On Oct 11,  8:53pm, Peter Stephenson wrote:
} Subject: Re: emulate with options other than -c is broken
}
} The next question is, do we want to use this for functions autoloaded
} by the completion system?  The argument against is that it changes
} the behaviour 

Hmm.  The difficulty I see with this is that compinit is used to load
widget functions (#compdef -k / -K) and other helpers (#autoload) for
which the set of $_comp_options do not necessarily apply.  Futhermore,
I know I use #autoload in files that don't necessarily have anything
to do with completion, just because I know compinit will pick them up
for me.  Pulling those setopts out of $_comp_setup into an emulate
wrapper around the autoload call in compinit (if that is indeed what
you contemplate here) would apply them too broadly.

So we'd probably need another #compdef switch to activate emulation
mode, and then go through all the completion functions to determine
which ones should have it.

} (we'd probably need to enter zsh emulation plus set a few options,
} rather than just set explicit options --- but note the property that
} if the emulation is the same we do allow option changes to propagate
} between functions, as at present)

I wonder if "the same" is the best comparison to make here.  As an
example, if I write

	emulate zsh -c 'doglob() { print * }'

it may not be my intention to prevent the extendedglob option from
being "passed down" from a calling context.  On the other hand

	emulate zsh -o no_extendedglob -c '...'

clearly means extendedglob should be forced off.  This would mean that
the behavior of non-sticky functions follows naturally -- when the set
of options turned on/off is empty, any/all options are "inherited".

However, that would mean storing not just the state of the options, but
the sense of how they arrived at that state, which is a lot of overhead.
And I guess the caller could sneak extendedglob through by doing

	emulate zsh -c 'setopt extendedglob; doglob'

because then the sticky bits are the same but I've changed extendedglob
after the fact.

} The argument in favour is the behaviour is easier to document because
} it's based on emulations, and the function behaviour is guaranteed
} wherever it's loaded or called, not just from somewhere under
} _main_complete

Hmm, maybe.  The function behavior would be guaranteed as long as you
didn't autoload those functions before compinit had a chance to do so.
But I suspect a lot of people autoload everything in $fpath, and that
at least some of them do so before calling compinit, particulary the
people who zcompile their entire function set.  So we'd still need to
perform all those setopts in _main_complete et al.

} (remember what got me into this was the fact that forcing _path_files
} to autoload outside the completion system gave an error).

Another potentially interesting question:  How should/does this interact
with zcompile?  For example the -U (noaliases) option is recorded in the
ZWC file, why shouldn't other option state be compilable, too?


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

end of thread, other threads:[~2012-10-13  4:02 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-05 23:01 emulate with options other than -c is broken Peter Stephenson
2012-10-07 19:26 ` Peter Stephenson
2012-10-11 19:53   ` Peter Stephenson
2012-10-13  4:01     ` Bart Schaefer

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