about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/options.yo23
-rw-r--r--Src/exec.c17
-rw-r--r--Src/options.c1
-rw-r--r--Src/zsh.h1
-rw-r--r--Test/E01options.ztst44
-rwxr-xr-xTest/ztst.zsh6
7 files changed, 90 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index 2b689b71d..6c4355e2f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2014-06-13  Peter Stephenson  <p.w.stephenson@ntlworld.com>
 
+	* 32768, with further modifications: Doc/Zsh/options.yo,
+	Src/exec.c, Src/options.c, 	Src/zsh.h, Test/E01options.ztst,
+	Test/ztst.zsh:  LOCAL_LOOPS option to restrict effect of
+	continue and break in function scope.
+
 	* 32666: Doc/Zsh/compat.yo: shell emulation based on executable
 	name incompletely documented.
 
@@ -18,6 +23,7 @@
 
 	* Jun T: 32755: Doc/Zsh/grammar.yo: move line that was in the
 	wrong place.
+
 2014-06-07  Barton E. Schaefer  <schaefer@zsh.org>
 
 	* Nikolas Garofil: 32737: Src/utils.c: properly ifdef declarations
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 7788cd755..9cb385e49 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -1631,6 +1631,21 @@ tt(FUNCTION_ARGZERO) from on to off (or off to on) does not change the
 current value of tt($0).  Only the state upon entry to the function or
 script has an effect.  Compare tt(POSIX_ARGZERO).
 )
+pindex(LOCAL_LOOPS)
+pindex(NO_LOCAL_LOOPS)
+pindex(LOCALLOOPS)
+pindex(NOLOCALLOOPS)
+cindex(break, inside function)
+cindex(continue, inside function)
+cinde(function, scope of break and continue)
+item(tt(LOCAL_LOOPS))(
+When this option is not set, the effect of tt(break) and tt(continue)
+commands may propagate outside function scope, affecting loops in
+calling functions.  When the option is set in a calling function, a
+tt(break) or a tt(continue) that is not caught within a called function
+(regardless of the setting of the option within that function)
+produces a warning and the effect is cancelled.
+)
 pindex(LOCAL_OPTIONS)
 pindex(NO_LOCAL_OPTIONS)
 pindex(LOCALOPTIONS)
@@ -1639,10 +1654,10 @@ item(tt(LOCAL_OPTIONS) <K>)(
 If this option is set at the point of return from a shell function,
 most options (including this one) which were in force upon entry to
 the function are restored; options that are not restored are
-tt(PRIVILEGED) and tt(RESTRICTED).  Otherwise, only this option and the
-tt(XTRACE) and tt(PRINT_EXIT_VALUE) options are restored.  Hence
-if this is explicitly unset by a shell function the other options in
-force at the point of return will remain so.
+tt(PRIVILEGED) and tt(RESTRICTED).  Otherwise, only this option,
+and the tt(LOCAL_LOOPS), tt(XTRACE) and tt(PRINT_EXIT_VALUE) options are
+restored.  Hence if this is explicitly unset by a shell function the
+other options in force at the point of return will remain so.
 A shell function can also guarantee itself a known shell configuration
 with a formulation like `tt(emulate -L zsh)'; the tt(-L) activates
 tt(LOCAL_OPTIONS).
diff --git a/Src/exec.c b/Src/exec.c
index 8249deff2..5ad957f98 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -4614,7 +4614,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
     char *name = shfunc->node.nam;
     int flags = shfunc->node.flags, ooflags;
     char *fname = dupstring(name);
-    int obreaks, saveemulation, restore_sticky;
+    int obreaks, ocontflag, oloops, saveemulation, restore_sticky;
     Eprog prog;
     struct funcstack fstack;
     static int oflags;
@@ -4626,7 +4626,9 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
     pushheap();
 
     oargv0 = NULL;
-    obreaks = breaks;;
+    obreaks = breaks;
+    ocontflag = contflag;
+    oloops = loops;
     if (trap_state == TRAP_STATE_PRIMED)
 	trap_return--;
     oldlastval = lastval;
@@ -4814,6 +4816,17 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	opts[XTRACE] = saveopts[XTRACE];
 	opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
 	opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
+	opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
+    }
+
+    if (opts[LOCALLOOPS]) {
+	if (contflag)
+	    zwarn("`continue' active at end of function scope");
+	if (breaks)
+	    zwarn("`break' active at end of function scope");
+	breaks = obreaks;
+	contflag = ocontflag;
+	loops = oloops;
     }
 
     endtrapscope();
diff --git a/Src/options.c b/Src/options.c
index 2163bff4d..6e4e7b911 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -180,6 +180,7 @@ static struct optname optns[] = {
 {{NULL, "listrowsfirst",      0},			 LISTROWSFIRST},
 {{NULL, "listtypes",	      OPT_ALL},			 LISTTYPES},
 {{NULL, "localoptions",	      OPT_EMULATE|OPT_KSH},	 LOCALOPTIONS},
+{{NULL, "localloops",	      OPT_EMULATE},		 LOCALLOOPS},
 {{NULL, "localpatterns",      OPT_EMULATE},		 LOCALPATTERNS},
 {{NULL, "localtraps",	      OPT_EMULATE|OPT_KSH},	 LOCALTRAPS},
 {{NULL, "login",	      OPT_SPECIAL},		 LOGINSHELL},
diff --git a/Src/zsh.h b/Src/zsh.h
index 05d582cda..fa7396112 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2129,6 +2129,7 @@ enum {
     LISTPACKED,
     LISTROWSFIRST,
     LISTTYPES,
+    LOCALLOOPS,
     LOCALOPTIONS,
     LOCALPATTERNS,
     LOCALTRAPS,
diff --git a/Test/E01options.ztst b/Test/E01options.ztst
index d9f219115..46b183776 100644
--- a/Test/E01options.ztst
+++ b/Test/E01options.ztst
@@ -430,7 +430,7 @@
   foo
   unfunction foo
 0:FUNCTION_ARGZERO option
->My name is ZTST_execchunk
+>My name is (anon)
 >My name is foo
 
   setopt _NO_glob_
@@ -1114,3 +1114,45 @@
 >1
 >1
 >2
+
+  for (( i = 0; i < 10; i++ )); do
+     () {
+        print $i
+        break
+     }
+  done
+0:NO_LOCAL_LOOPS
+>0
+
+  () {
+      emulate -L zsh
+      setopt localloops
+      for (( i = 0; i < 10; i++ )); do
+	  () {
+              setopt nolocalloops # ignored in parent
+              print $i
+              break
+	  }
+      done
+  }
+0:LOCAL_LOOPS
+>0
+>1
+>2
+>3
+>4
+>5
+>6
+>7
+>8
+>9
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
+?(anon):4: `break' active at end of function scope
diff --git a/Test/ztst.zsh b/Test/ztst.zsh
index 745a13cff..74111f6cc 100755
--- a/Test/ztst.zsh
+++ b/Test/ztst.zsh
@@ -260,8 +260,12 @@ $ZTST_redir"
 # Execute an indented chunk.  Redirections will already have
 # been set up, but we need to handle the options.
 ZTST_execchunk() {
+  setopt localloops # don't let continue & break propagate out
   options=($ZTST_testopts)
-  eval "$ZTST_code"
+  () {
+      unsetopt localloops
+      eval "$ZTST_code"
+  }
   ZTST_status=$?
   # careful... ksh_arrays may be in effect.
   ZTST_testopts=(${(kv)options[*]})