zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: `try' syntax
@ 2004-06-14 11:23 Peter Stephenson
  2004-06-14 12:58 ` Oliver Kiddle
  0 siblings, 1 reply; 13+ messages in thread
From: Peter Stephenson @ 2004-06-14 11:23 UTC (permalink / raw)
  To: Zsh hackers list

Here's an experimental and possibly controversial (though, I think,
working) new syntax for running code protected from error and other
conditions in an internal block.  Dave Yost suggested something like
this, but I've been missing it for a long time.  Traps are too clumsy to
provide good protection against errors or interrupts.

The basic idea is:

  :try
     # Code here runs normally
  :always
     # Code here always gets run, even if there is a fatal
     # error (unless it crashes the shell), or a return, break,
     # or continue in the :try block.
     #
     # Optionally:
     unset -e
     # removes any error condition.  Apart from that, after exiting
     # the always block, the logic is as if it hadn't been present.
  :tried

Thus it's a bit more like an unwind-protect in Lisp than exception
catching, which is why I deliberately avoided using `catch' in the
name.  (The name `always' was probably suggested by Verilog but
it has a completely different meaning.)

The trickiest bit is the naming.  I wanted something reasonably
memorable, but without polluting the name space, hence the names
with the colons in front.  Note that `disable -r :try :always :tried'
works.

Index: Doc/Zsh/builtins.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/builtins.yo,v
retrieving revision 1.68
diff -u -r1.68 builtins.yo
--- Doc/Zsh/builtins.yo	21 May 2004 20:04:59 -0000	1.68
+++ Doc/Zsh/builtins.yo	14 Jun 2004 11:11:22 -0000
@@ -1472,8 +1472,10 @@
 )
 findex(unset)
 cindex(parameters, unsetting)
-item(tt(unset) [ tt(-fmv) ] var(name) ...)(
-Each named parameter is unset.
+cindex(error condition, resetting)
+xitem(tt(unset) [ tt(-fmv) ] var(name) ...)
+item(tt(unset -e))(
+In the first form, each named parameter is unset.
 Local parameters remain local even if unset; they appear unset within scope,
 but the previous value will still reappear when the scope ends.
 
@@ -1490,6 +1492,11 @@
 default behaviour.
 
 tt(unset -f) is equivalent to tt(unfunction).
+
+tt(unset -e) may only be used in an tt(:always) block of a tt(:try)
+construct, as described in
+ifzman(the section em(Complex Commands) in zmanref(zshmisc))\
+ifnzman(noderef(Complex Commands))
 )
 findex(unsetopt)
 cindex(options, unsetting)
Index: Doc/Zsh/grammar.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/grammar.yo,v
retrieving revision 1.8
diff -u -r1.8 grammar.yo
--- Doc/Zsh/grammar.yo	24 Jul 2001 13:16:09 -0000	1.8
+++ Doc/Zsh/grammar.yo	14 Jun 2004 11:11:26 -0000
@@ -250,6 +250,33 @@
 there is a single var(word);  otherwise, the parentheses will be treated as
 forming a globbing pattern in that case.
 )
+findex(:always)
+findex(:try)
+cindex(always blocks)
+cindex(try blocks)
+item(tt(:try) var(try-list) tt(:always) var(always-list) tt(:tried))(
+First var(try-list) is executed.  Regardless of errors, or tt(break),
+tt(continue), or tt(return) commands encountered within var(try-list),
+var(always-list) is then executed.  Execution then continues from the
+result of the execution of var(try-list); in other words, any error,
+or tt(break), tt(continue), or tt(return) command is treated in the
+normal way, as if the tt(:try) statement and tt(:always) block were not
+present.
+
+Newlines or semicolons may optionally appear between the tt(:try:)
+or tt(:always:) and the following list.
+
+The command `tt(unset -e)' may be used within the tt(:always) block to
+unset any error condition caused by the tt(:try) block.  Hence the
+following idiom causes errors in var(commands...) to be ignored, without
+the need to run var(commands...) in a subshell.
+
+example(:try
+  var(commands)
+:always
+  unset -e
+:tried)
+)
 cindex(timing)
 findex(time)
 item(tt(time) [ var(pipeline) ])(
Index: Functions/MIME/zsh-mime-setup
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/MIME/zsh-mime-setup,v
retrieving revision 1.1
diff -u -r1.1 zsh-mime-setup
--- Functions/MIME/zsh-mime-setup	14 Sep 2003 19:37:35 -0000	1.1
+++ Functions/MIME/zsh-mime-setup	14 Jun 2004 11:11:26 -0000
@@ -62,9 +62,8 @@
 zstyle -a :mime: mailcap cap_files ||
   cap_files=(~/.mailcap /etc/mailcap)
 
-TRAPEXIT() { unfunction mime-setup-add-type >&/dev/null; return 0; }
-
-mime-setup-add-type() {
+:try
+  mime-setup-add-type() {
     local type suffix
     local -a array
 
@@ -90,115 +89,117 @@
 	    fi
 	fi
     done
-}
+  }
 
-# Loop through files to find suffixes for MIME types.
-# Earlier entries take precedence, so the files need to be listed
-# with the user's own first.  This also means pre-existing
-# values in suffix_type_map are respected.
-for file in $type_files; do
+  # Loop through files to find suffixes for MIME types.
+  # Earlier entries take precedence, so the files need to be listed
+  # with the user's own first.  This also means pre-existing
+  # values in suffix_type_map are respected.
+  for file in $type_files; do
     [[ -r $file ]] || continue
 
     # For once we rely on the fact that read handles continuation
     # lines ending in backslashes, i.e. there's no -r.
     while read line; do
-	# Skip blank or comment lines.
-	[[ $line = [[:space:]]#(\#*|) ]] && continue
-
-	# There are two types of line you find in MIME type files.
-	# The original simple sort contains the type name then suffixes
-	# separated by whitespace.  However, Netscape insists
-	# on adding lines with backslash continuation with
-	# key="value" pairs.  So we'd better handle both.
-	if [[ $line = *=* ]]; then
-	    # Gory.
-	    # This relies on the fact that a typical entry:
-	    #   type=video/x-mpeg2 desc="MPEG2 Video" exts="mpv2,mp2v"
-	    # looks like a parameter assignment.  However, we really
-	    # don't want to be screwed up by future extensions,
-	    # so we split the elements to an array and pick out the
-	    # ones we're interested in.
-	    type= exts=
-
-	    # Syntactically split line to preserve quoted words.
-	    array=(${(z)line})
-	    for elt in $array; do
-		if [[ $elt = (type|exts)=* ]]; then
-		    eval $elt
-		fi
-	    done
+      # Skip blank or comment lines.
+      [[ $line = [[:space:]]#(\#*|) ]] && continue
 
-	    # Get extensions by splitting on comma
-	    array=(${(s.,.)exts})
-
-	    [[ -n $type ]] && mime-setup-add-type $type $array
-	else
-	    # Simple.
-	    mime-setup-add-type ${=line}
-	fi
+      # There are two types of line you find in MIME type files.
+      # The original simple sort contains the type name then suffixes
+      # separated by whitespace.  However, Netscape insists
+      # on adding lines with backslash continuation with
+      # key="value" pairs.  So we'd better handle both.
+      if [[ $line = *=* ]]; then
+        # Gory.
+        # This relies on the fact that a typical entry:
+        #   type=video/x-mpeg2 desc="MPEG2 Video" exts="mpv2,mp2v"
+        # looks like a parameter assignment.  However, we really
+        # don't want to be screwed up by future extensions,
+        # so we split the elements to an array and pick out the
+        # ones we're interested in.
+        type= exts=
+
+        # Syntactically split line to preserve quoted words.
+        array=(${(z)line})
+        for elt in $array; do
+          if [[ $elt = (type|exts)=* ]]; then
+            eval $elt
+          fi
+        done
+
+        # Get extensions by splitting on comma
+        array=(${(s.,.)exts})
+
+        [[ -n $type ]] && mime-setup-add-type $type $array
+      else
+        # Simple.
+        mime-setup-add-type ${=line}
+      fi
     done <$file
-done
-
+  done
+:always
+  unfunction mime-setup-add-type >&/dev/null
+:tried
 
 # Loop through files to find handlers for types.
 for file in $cap_files; do
-    [[ -r $file ]] || continue
+  [[ -r $file ]] || continue
 
-    # Oh, great.  We need to preserve backslashes inside the line,
-    # but need to manage continuation lines.
-    while read -r line; do
-	# Skip blank or comment lines.
-	[[ $line = [[:space:]]#(\#*|) ]] && continue
-
-	while [[ $line = (#b)(*)\\ ]]; do
-	    line=$match[1]
-	    read -r line2 || break
-	    line+=$line2
-	done
-
-	# Guess what, this file has a completely different format.
-	# See mailcap(4).
-	# The biggest unpleasantness here is that the fields are
-	# delimited by semicolons, but the command field, which
-	# is the one we want to extract, may itself contain backslashed
-	# semicolons.
-	if [[ $line = (#b)[[:space:]]#([^[:space:]\;]##)[[:space:]]#\;(*) ]]
-	then
-	    # this is the only form we can handle, but there's no point
-	    # issuing a warning for other forms.
-	    type=$match[1]
-            line=$match[2]
-	    # See if it has flags after the command.
-	    if [[ $line = (#b)(([^\;\\]|\\\;|\\[^\;])#)\;(*) ]]; then
-		line=$match[1]
-		flags=$match[3]
-	    else
-		flags=
-	    fi
-	    # Remove quotes from semicolons
-	    line=${line//\\\;/\;}
-	    # and remove any surrounding white space --- this might
-	    # make the handler empty.
-	    line=${${line##[[:space:]]#}%%[[:space:]]}
-	    if [[ -z $type_handler_map[$type] ]]; then
-		if [[ -n $o_verbose ]]; then
-		    print -r "Adding handler for type $type:
+  # Oh, great.  We need to preserve backslashes inside the line,
+  # but need to manage continuation lines.
+  while read -r line; do
+    # Skip blank or comment lines.
+    [[ $line = [[:space:]]#(\#*|) ]] && continue
+
+    while [[ $line = (#b)(*)\\ ]]; do
+      line=$match[1]
+      read -r line2 || break
+      line+=$line2
+    done
+
+    # Guess what, this file has a completely different format.
+    # See mailcap(4).
+    # The biggest unpleasantness here is that the fields are
+    # delimited by semicolons, but the command field, which
+    # is the one we want to extract, may itself contain backslashed
+    # semicolons.
+    if [[ $line = (#b)[[:space:]]#([^[:space:]\;]##)[[:space:]]#\;(*) ]]
+    then
+      # this is the only form we can handle, but there's no point
+      # issuing a warning for other forms.
+      type=$match[1]
+      line=$match[2]
+      # See if it has flags after the command.
+      if [[ $line = (#b)(([^\;\\]|\\\;|\\[^\;])#)\;(*) ]]; then
+        line=$match[1]
+        flags=$match[3]
+      else
+        flags=
+      fi
+      # Remove quotes from semicolons
+      line=${line//\\\;/\;}
+      # and remove any surrounding white space --- this might
+      # make the handler empty.
+      line=${${line##[[:space:]]#}%%[[:space:]]}
+      if [[ -z $type_handler_map[$type] ]]; then
+        if [[ -n $o_verbose ]]; then
+          print -r "Adding handler for type $type:
   $line" >&2
-		fi
-		type_handler_map[$type]=$line
-		type_flags_map[$type]=$flags
-		if [[ -n $flags && -n $o_verbose ]]; then
-		    print -r "  with flags $flags" >&2
-		fi
-	    elif [[ -n $o_verbose ]]; then
-		print -r "Skipping handler for already defined type $type:
+	fi
+	type_handler_map[$type]=$line
+	type_flags_map[$type]=$flags
+	if [[ -n $flags && -n $o_verbose ]]; then
+	  print -r "  with flags $flags" >&2
+	fi
+      elif [[ -n $o_verbose ]]; then
+	print -r "Skipping handler for already defined type $type:
   $line" >&2
-		if [[ -n $flags ]]; then
-		    print -r " with flags $flags" >&2
-		fi
-	    fi
+	if [[ -n $flags ]]; then
+	  print -r " with flags $flags" >&2
 	fi
-    done <$file
+      fi
+    fi
+  done <$file
 done
 
 
Index: Src/builtin.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/builtin.c,v
retrieving revision 1.121
diff -u -r1.121 builtin.c
--- Src/builtin.c	2 Jun 2004 22:14:25 -0000	1.121
+++ Src/builtin.c	14 Jun 2004 11:11:34 -0000
@@ -125,7 +125,7 @@
     BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "ms", "a"),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
     BUILTIN("unhash", 0, bin_unhash, 1, -1, 0, "adfms", NULL),
-    BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, 0, "fmv", NULL),
+    BUILTIN("unset", BINF_PSPECIAL, bin_unset, 0, -1, 0, "efmv", NULL),
     BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL),
     BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL),
     BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsw", NULL),
@@ -2614,6 +2614,23 @@
     int match = 0, returnval = 0;
     int i;
 
+    if (OPT_ISSET(ops,'e')) {
+	if (*argv) {
+	    zwarnnam(name, "arguments not allowed with -e", NULL, 0);
+	    return 1;
+	}
+	if (try_errflag < 0) {
+	    zwarnnam(name, "-e can only be used in an :always block", NULL, 0);
+	    return 1;
+	}
+	try_errflag = 0;
+	return 0;
+    }
+    if (!*argv) {
+	zwarnnam(name, "not enough arguments", NULL, 0);
+	return 1;
+    }
+
     /* unset -f is the same as unfunction */
     if (OPT_ISSET(ops,'f'))
 	return bin_unhash(name, argv, ops, func);
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.64
diff -u -r1.64 exec.c
--- Src/exec.c	2 Jun 2004 22:14:25 -0000	1.64
+++ Src/exec.c	14 Jun 2004 11:11:39 -0000
@@ -137,10 +137,10 @@
 
 /* Execution functions. */
 
-static int (*execfuncs[]) _((Estate, int)) = {
+static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
     execcursh, exectime, execfuncdef, execfor, execselect,
     execwhile, execrepeat, execcase, execif, execcond,
-    execarith, execautofn
+    execarith, execautofn, exectry
 };
 
 /* structure for command builtin for when it is used with -v or -V */
Index: Src/hashtable.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/hashtable.c,v
retrieving revision 1.20
diff -u -r1.20 hashtable.c
--- Src/hashtable.c	2 Jun 2004 22:14:25 -0000	1.20
+++ Src/hashtable.c	14 Jun 2004 11:11:40 -0000
@@ -946,6 +946,9 @@
     {NULL, "time", 0, TIME},
     {NULL, "until", 0, UNTIL},
     {NULL, "while", 0, WHILE},
+    {NULL, ":try", 0, TRY},
+    {NULL, ":always", 0, ALWAYS},
+    {NULL, ":tried", 0, TRIED},
     {NULL, NULL, 0, 0}
 };
 
Index: Src/loop.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/loop.c,v
retrieving revision 1.13
diff -u -r1.13 loop.c
--- Src/loop.c	2 Jun 2004 22:14:26 -0000	1.13
+++ Src/loop.c	14 Jun 2004 11:11:41 -0000
@@ -616,3 +616,64 @@
 
     return lastval;
 }
+
+/* Errflag from :try block, may be reset in :always block. */
+
+/**/
+int
+try_errflag = -1;
+
+/**/
+int
+exectry(Estate state, int do_exec)
+{
+    Wordcode end, always;
+    int endval;
+    int save_try_errflag, save_retflag, save_breaks, save_loops, save_contflag;
+
+    end = state->pc + WC_TRY_SKIP(state->pc[-1]);
+    always = state->pc + 1 + WC_TRY_SKIP(*state->pc);
+    state->pc++;
+    pushheap();
+    cmdpush(CS_TRY);
+
+    /* The :try clause */
+    execlist(state, 1, do_exec);
+
+    /* Don't record errflag here, may be reset. */
+    endval = lastval;
+
+    freeheap();
+
+    cmdpop();
+    cmdpush(CS_ALWAYS);
+
+    /* The :always clause. */
+    save_try_errflag = try_errflag;
+    try_errflag = errflag;
+    errflag = 0;
+    save_retflag = retflag;
+    retflag = 0;
+    save_breaks = breaks;
+    breaks = 0;
+    save_loops = loops;
+    loops = 0;
+    save_contflag = contflag;
+    contflag = 0;
+
+    state->pc = always;
+    execlist(state, 1, do_exec);
+
+    errflag = try_errflag;
+    try_errflag = save_try_errflag;
+    retflag = save_retflag;
+    breaks = save_breaks;
+    loops = save_loops;
+    contflag = save_contflag;
+
+    cmdpop();
+    popheap();
+    state->pc = end;
+
+    return endval || errflag;
+}
Index: Src/parse.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/parse.c,v
retrieving revision 1.44
diff -u -r1.44 parse.c
--- Src/parse.c	2 Jun 2004 22:14:26 -0000	1.44
+++ Src/parse.c	14 Jun 2004 11:11:43 -0000
@@ -760,7 +760,7 @@
 }
 
 /*
- * cmd	: { redir } ( for | case | if | while | repeat |
+ * cmd	: { redir } ( for | case | if | while | repeat | try
  *				subsh | funcdef | time | dinbrack | dinpar | simple ) { redir }
  */
 
@@ -819,6 +819,11 @@
 	par_repeat(complex);
 	cmdpop();
 	break;
+    case TRY:
+	cmdpush(CS_TRY);
+	par_try(complex);
+	cmdpop();
+	break;
     case INPAR:
 	*complex = 1;
 	cmdpush(CS_SUBSH);
@@ -1330,6 +1335,49 @@
 }
 
 /*
+ * try          : TRY list ALWAYS list TRIED
+ */
+
+/**/
+static void
+par_try(int *complex)
+{
+    int oecused = ecused, p, pp;
+
+    p = ecadd(0);
+    pp = ecadd(0);
+
+    do {
+	yylex();
+    } while (tok == SEPER);
+
+    par_save_list(complex);
+    ecbuf[pp] = WCB_TRY(ecused - 1 - pp);
+
+    incmdpos = 1;
+
+    while (tok == SEPER)
+	yylex();
+
+    if (tok != ALWAYS)
+	YYERRORV(oecused);
+
+    cmdpop();
+    cmdpush(CS_ALWAYS);
+    yylex();
+    par_save_list(complex);
+    while (tok == SEPER)
+	yylex();
+
+    incmdpos = 1;
+
+    if (tok != TRIED)
+	YYERRORV(oecused);
+    yylex();
+    ecbuf[p] = WCB_TRY(ecused - 1 - p);
+}
+
+/*
  * subsh	: ( INPAR | INBRACE ) list ( OUTPAR | OUTBRACE )
  */
 
Index: Src/prompt.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/prompt.c,v
retrieving revision 1.18
diff -u -r1.18 prompt.c
--- Src/prompt.c	28 May 2004 19:21:46 -0000	1.18
+++ Src/prompt.c	14 Jun 2004 11:11:44 -0000
@@ -49,7 +49,7 @@
 
 /* parser states, for %_ */
 
-static char *cmdnames[] = {
+static char *cmdnames[CS_COUNT] = {
     "for",      "while",     "repeat",    "select",
     "until",    "if",        "then",      "else",
     "elif",     "math",      "cond",      "cmdor",
@@ -57,7 +57,8 @@
     "case",     "function",  "subsh",     "cursh",
     "array",    "quote",     "dquote",    "bquote",
     "cmdsubst", "mathsubst", "elif-then", "heredoc",
-    "heredocd", "brace",     "braceparam",
+    "heredocd", "brace",     "braceparam", ":try",
+    ":always"
 };
  
 /* The buffer into which an expanded and metafied prompt is being written, *
Index: Src/text.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/text.c,v
retrieving revision 1.13
diff -u -r1.13 text.c
--- Src/text.c	4 Feb 2003 11:23:05 -0000	1.13
+++ Src/text.c	14 Jun 2004 11:11:45 -0000
@@ -721,6 +721,31 @@
 	    taddstr("))");
 	    stack = 1;
 	    break;
+	case WC_TRY:
+	    if (!s) {
+		taddstr(":try");
+		/*
+		 * skip location of end of first list, we will
+		 * go all the way through it since we're just printing.
+		 */
+		state->pc++;
+		tindent++;
+		taddnl();
+		tpush(code, 0);
+	    } else if (!s->pop) {
+		tindent--;
+		taddnl();
+		taddstr(":always");
+		tindent++;
+		taddnl();
+		s->pop = 1;
+	    } else {
+		tindent--;
+		taddnl();
+		taddstr(":tried");
+		stack = 1;
+	    }
+	    break;
 	case WC_END:
 	    stack = 1;
 	    break;
Index: Src/zsh.h
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v
retrieving revision 1.56
diff -u -r1.56 zsh.h
--- Src/zsh.h	20 Apr 2004 12:11:16 -0000	1.56
+++ Src/zsh.h	14 Jun 2004 11:11:47 -0000
@@ -226,7 +226,10 @@
     THEN,	/* then      */
     TIME,	/* time      */
     UNTIL,	/* until     */ /* 60 */
-    WHILE	/* while     */
+    WHILE,	/* while     */
+    TRY,	/* :try      */
+    ALWAYS,	/* :always   */
+    TRIED	/* :tried    */
 };
 
 /* Redirection types.  If you modify this, you may also have to modify *
@@ -580,6 +583,10 @@
 #define WC_COND    17
 #define WC_ARITH   18
 #define WC_AUTOFN  19
+#define WC_TRY     20
+
+/* increment as necessary */
+#define WC_COUNT   21
 
 #define WCB_END()           wc_bld(WC_END, 0)
 
@@ -657,6 +664,9 @@
 #define WC_REPEAT_SKIP(C)   wc_data(C)
 #define WCB_REPEAT(O)       wc_bld(WC_REPEAT, (O))
 
+#define WC_TRY_SKIP(C)	    wc_data(C)
+#define WCB_TRY(O)	    wc_bld(WC_TRY, (O))
+
 #define WC_CASE_TYPE(C)     (wc_data(C) & 3)
 #define WC_CASE_HEAD        0
 #define WC_CASE_OR          1
@@ -1695,6 +1705,11 @@
 #define CS_HEREDOCD    28
 #define CS_BRACE       29
 #define CS_BRACEPAR    30
+#define CS_TRY	       31
+#define CS_ALWAYS      32
+
+/* Increment as necessary */
+#define CS_COUNT       33
 
 /*********************
  * Memory management *

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 692070


**********************************************************************
This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the system manager.

This footnote also confirms that this email message has been swept by
MIMEsweeper for the presence of computer viruses.

www.mimesweeper.com
**********************************************************************


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

* Re: PATCH: `try' syntax
  2004-06-14 11:23 PATCH: `try' syntax Peter Stephenson
@ 2004-06-14 12:58 ` Oliver Kiddle
  2004-06-14 13:52   ` Peter Stephenson
  0 siblings, 1 reply; 13+ messages in thread
From: Oliver Kiddle @ 2004-06-14 12:58 UTC (permalink / raw)
  To: Zsh hackers list

Peter wrote:

> The basic idea is:
> 
>   :try
>      # Code here runs normally
>   :always
>      # Code here always gets run, even if there is a fatal
>      # error (unless it crashes the shell), or a return, break,
>      # or continue in the :try block.

What exactly do break, continue and return do here? Are they unchanged,
expecting an enclosing loop or function. It'd be really useful to have
a way to skip over the rest of the try block, going straight to the
always code. The best way to handle that would be to use break with the
try block appearing to be a loop. But we probably don't want break and
continue numbers to be out of sync so perhaps both should have that
effect.

Is it possible to put the command after try and always. e.g:
  :try do-stuff; :always rm $tempfile; :tried

>      #
>      # Optionally:
>      unset -e
>      # removes any error condition.  Apart from that, after exiting
>      # the always block, the logic is as if it hadn't been present.

What sort of things does an "error condition" encompass? Is it just
exit signals? It isn't clear without an example. It could be useful to
have something like the errexit and errreturn options apply within the
try block.

`unset -e' seems a bit obscure. Would an option to exit or return make
it clearer what is happening. Or how about using special variable which
indicates what the "error condition" is and have that be unset.

> The trickiest bit is the naming.  I wanted something reasonably
> memorable, but without polluting the name space, hence the names
> with the colons in front.  Note that `disable -r :try :always :tried'
> works.

The colons make them look like labels. You could avoid `tried' by using
`done' or, being consistent with esac and fi, use `yrt'. Another option
could be to use uppercase. Or could it go in a module?

We ought to fix completion to do something about colons in
command-names in the current context. What you see with:
: <Ctrl-x><h>
is not ideal.

Does this actually do anything which traps can't do? Can we perhaps
change something about traps to make them simpler to use. It doesn't
look quite as versatile, for example you can't setup timeout delays.

Oliver


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

* Re: PATCH: `try' syntax
  2004-06-14 12:58 ` Oliver Kiddle
@ 2004-06-14 13:52   ` Peter Stephenson
  2004-06-14 16:55     ` Oliver Kiddle
  0 siblings, 1 reply; 13+ messages in thread
From: Peter Stephenson @ 2004-06-14 13:52 UTC (permalink / raw)
  To: Zsh hackers list

Oliver Kiddle wrote:
> What exactly do break, continue and return do here? Are they unchanged,
> expecting an enclosing loop or function. 

Yes.  The description in the manual is supposed to say that.

> It'd be really useful to have
> a way to skip over the rest of the try block, going straight to the
> always code. The best way to handle that would be to use break with the
> try block appearing to be a loop. But we probably don't want break and
> continue numbers to be out of sync so perhaps both should have that
> effect.

I thought about that, but it thoroughly obscures the effect of breaks
and continues.

> Is it possible to put the command after try and always. e.g:
>   :try do-stuff; :always rm $tempfile; :tried

Yes, as it says in the manual entry the breaks afterward are optional.

> What sort of things does an "error condition" encompass? Is it just
> exit signals? It isn't clear without an example.

Whenever errflag is 1 internally: a brief summary is it is set for
syntax errors and gross failures to carry out user requests.  In other
words, anything which would case the rest of the code to be skipped
without a user command (return etc.).  One example couldn't make that
clear, but would probably be useful anyway.

> It could be useful to
> have something like the errexit and errreturn options apply within the
> try block.

They do, but to the enclosing function (the always block would be
executed).  Again, trying to make the try block do something more than
its basic purpose seemed to me a bad idea.  I specifically designed it
*not* to change the basic flow.

> `unset -e' seems a bit obscure. Would an option to exit or return make
> it clearer what is happening.

No, for the same reason.  It's not an exit or a return.

> Or how about using special variable which
> indicates what the "error condition" is and have that be unset.

Yes, that's possible, and it allows you to test for an error, too, which
the current syntax doesn't.  Again, I couldn't think of a good name
which didn't potentially clash with a user variable.  Perhaps TRY_ERROR
or even TRY_BLOCK_ERROR would be good enough.  (It's not a status;
$? correctly indicates the status after the try block, but not whether
an error occurred.)

(I wonder if it could be setable inside the try block to force errflag
to 1, forcing the rest of the block to be skipped?  That would give a
nice interaction with traps.  This is getting dangerously close to
`throw'.)

> The colons make them look like labels.

Not in any UNIX shell I've ever seen.

> You could avoid `tried' by using
> `done' or, being consistent with esac and fi, use `yrt'. Another option
> could be to use uppercase.

That doesn't remove the basic problem, although upper case is a good
idea.

> Or could it go in a module?

Not without a lot of nasty rewriting and potential inconsistency.  It
*could* be done, though.

> Does this actually do anything which traps can't do? Can we perhaps
> change something about traps to make them simpler to use.

Traps aren't actually designed for this purpose at all.  There's no way
of making sure a trap gets executed, because there wasn't supposed to be
--- they give code to run under particular circumstances, not a
catch-all which does nothing but ensure a chunk of code is always
executed.

There's no easy way of restricting a trap to a block scope, either, so
even a new trap set at a similar point wouldn't really do the job.  The
point about the new syntax is it's structural, not event-driven.  This
is a key difference for the type of use I envisage (and indeed have
wanted in the past).

You *could* probably add a special unconditional exit trap and require
people to embed stuff in functions if they want to use it.  It's
certainly not simpler, though, and it won't have the effect of allowing
breaks and continues to apply to surrounding loops.

> It doesn't
> look quite as versatile, for example you can't setup timeout delays.

It's not supposed to be, it's for a quite different purpose.  However,
you could probably mix a timeout with the use of an always block.
That's part of the reason for wondering whether the error flag should be
settable.

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 692070


**********************************************************************
This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the system manager.

This footnote also confirms that this email message has been swept by
MIMEsweeper for the presence of computer viruses.

www.mimesweeper.com
**********************************************************************


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

* Re: PATCH: `try' syntax
  2004-06-14 13:52   ` Peter Stephenson
@ 2004-06-14 16:55     ` Oliver Kiddle
  2004-06-14 17:27       ` Peter Stephenson
  2004-06-14 18:14       ` Bart Schaefer
  0 siblings, 2 replies; 13+ messages in thread
From: Oliver Kiddle @ 2004-06-14 16:55 UTC (permalink / raw)
  To: Zsh hackers list

Peter wrote:
> Oliver Kiddle wrote:
> > What exactly do break, continue and return do here? Are they unchanged,
> > expecting an enclosing loop or function. 
> 
> Yes.  The description in the manual is supposed to say that.

The manual description should probably also mention exit along with
break, continue and return.

What happens if break, continue, return or exit are used within the
always block?

> > Or how about using special variable which
> > indicates what the "error condition" is and have that be unset.
> 
> Yes, that's possible, and it allows you to test for an error, too, which
> the current syntax doesn't.  Again, I couldn't think of a good name
> which didn't potentially clash with a user variable.  Perhaps TRY_ERROR
> or even TRY_BLOCK_ERROR would be good enough.  (It's not a status;
> $? correctly indicates the status after the try block, but not whether
> an error occurred.)

TRY_ERROR sounds fine though I might go for TRY_STATUS if it can
indicate a lack of error. Testing for an error could certainly be
useful. Does $? get unchanged by the always block like with a trap or
is that only if return or exit is used with an explicit status.

> (I wonder if it could be setable inside the try block to force errflag
> to 1, forcing the rest of the block to be skipped?  That would give a
> nice interaction with traps.  This is getting dangerously close to
> `throw'.)

That's an interesting idea. Would be useful. Having a variable
assignment affect control flow would be more than a bit weird, though.
And without making TRY_ERROR an array, you couldn't skip out of nested
try blocks.

> > The colons make them look like labels.
> 
> Not in any UNIX shell I've ever seen.

DOS batch files have labels in that form and given their lack of any
decent control flow statements, you need to use gotos a lot in them. It
also isn't so dissimilar to other languages' (including the C shell)
use of a trailing colon for labels or named blocks. So when I look at
them, I think label.

Oliver


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

* Re: PATCH: `try' syntax
  2004-06-14 16:55     ` Oliver Kiddle
@ 2004-06-14 17:27       ` Peter Stephenson
  2004-06-14 18:14       ` Bart Schaefer
  1 sibling, 0 replies; 13+ messages in thread
From: Peter Stephenson @ 2004-06-14 17:27 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: Zsh hackers list

Oliver Kiddle wrote:
> Peter wrote:
> > Oliver Kiddle wrote:
> > > What exactly do break, continue and return do here? Are they unchanged,
> > > expecting an enclosing loop or function. 
> > 
> > Yes.  The description in the manual is supposed to say that.
> 
> The manual description should probably also mention exit along with
> break, continue and return.

It doesn't handle exits, exits are immediate.  That's why it doesn't
mention it.

> What happens if break, continue, return or exit are used within the
> always block?

They are ignored (except exit).  Statuses generally use the value from
the try block.

> > > Or how about using special variable which
> > > indicates what the "error condition" is and have that be unset.
> > 
> > Yes, that's possible, and it allows you to test for an error, too, which
> > the current syntax doesn't.  Again, I couldn't think of a good name
> > which didn't potentially clash with a user variable.  Perhaps TRY_ERROR
> > or even TRY_BLOCK_ERROR would be good enough.  (It's not a status;
> > $? correctly indicates the status after the try block, but not whether
> > an error occurred.)
> 
> TRY_ERROR sounds fine though I might go for TRY_STATUS if it can
> indicate a lack of error.

But it exactly indicates whether or not there's an error.  $? indicates
the status.

> Does $? get unchanged by the always block like with a trap or
> is that only if return or exit is used with an explicit status.

$? is like the other variables, it's restored after the always block
to what it was in the try block.  (Internally, the exectry returns the
try block status or'd with the error flag --- I think that's redundant
but it seemed safer --- which gets stored in lastval.)

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 692070


**********************************************************************
This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the system manager.

This footnote also confirms that this email message has been swept by
MIMEsweeper for the presence of computer viruses.

www.mimesweeper.com
**********************************************************************


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

* Re: PATCH: `try' syntax
  2004-06-14 16:55     ` Oliver Kiddle
  2004-06-14 17:27       ` Peter Stephenson
@ 2004-06-14 18:14       ` Bart Schaefer
  2004-06-15 10:31         ` Oliver Kiddle
                           ` (2 more replies)
  1 sibling, 3 replies; 13+ messages in thread
From: Bart Schaefer @ 2004-06-14 18:14 UTC (permalink / raw)
  To: Zsh hackers list

On Mon, 14 Jun 2004, Peter Stephenson wrote:

> Here's an experimental and possibly controversial (though, I think,
> working) new syntax for running code protected from error and other
> conditions in an internal block.
[...]
> 
> The trickiest bit is the naming.  I wanted something reasonably
> memorable, but without polluting the name space, hence the names
> with the colons in front.

I have several suggestions, in no particular order (and I'm deleting the
ones that overlap with Oliver as I find them).

* I'm not thrilled with "try" and "always" but I haven't yet come up with
anything better.

* A "shortloops" form "try { ... }" might be nice, or maybe should be the
only form.

* How about making this work with any loop body by replacing "do" with
"try"?  Then get rid of "try" as a standalone reserved word and instead
use "repeat 1; try ... always ... done"

* Rather than putting colons or some other unlikely character in front of
the name, use plain words and start with them "disable"d, so that in order
to use this syntax one must first "enable -r try always tried".  (This
technique could apply to other extension syntax as well.)

* Tangential thought: Is it really necessary to disable e.g. both "case"
and "esac" or is it sufficient to disable (and enable) "case"?

On Mon, 14 Jun 2004, Peter Stephenson wrote:

> Oliver Kiddle wrote:
> > It could be useful to
> > have something like the errexit and errreturn options apply within the
> > try block.
> 
> They do, but to the enclosing function (the always block would be
> executed).

So does that mean that

   :try
     setopt errexit
     false
     print not reached 1
   :always
     print reached
   :tried
   print not reached 2

prints only "reached"?  Then that's a non-obvious way to accomplish what
Oliver wanted:

> > It'd be really useful to have a way to skip over the rest of the try
> > block, going straight to the always code.

On Mon, 14 Jun 2004, Oliver Kiddle wrote:

> TRY_ERROR sounds fine though I might go for TRY_STATUS
> 
> > (I wonder if it could be setable inside the try block to force errflag
> > to 1, forcing the rest of the block to be skipped? [...])
> 
> That's an interesting idea. Would be useful. Having a variable
> assignment affect control flow would be more than a bit weird, though.

I agree that it would be weird.  Another new keyword would be better
(see my suggestion about starting disabled).


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

* Re: PATCH: `try' syntax
  2004-06-14 18:14       ` Bart Schaefer
@ 2004-06-15 10:31         ` Oliver Kiddle
  2004-06-15 10:51         ` DervishD
  2004-06-15 13:33         ` Peter Stephenson
  2 siblings, 0 replies; 13+ messages in thread
From: Oliver Kiddle @ 2004-06-15 10:31 UTC (permalink / raw)
  To: Zsh hackers list

Bart wrote:
> * A "shortloops" form "try { ... }" might be nice, or maybe should be the
> only form.

Or, thinking along those lines, you could use something like
{ ... } always { ... }
That currently finds a syntax error at always.

> * Rather than putting colons or some other unlikely character in front of
> the name, use plain words and start with them "disable"d, so that in order
> to use this syntax one must first "enable -r try always tried".  (This
> technique could apply to other extension syntax as well.)

That would be quite a good way of handling it. My main reservation
concerns the fact that it isn't possible to make the enabled/disabled
state of builtins local.

Consider _approximate. It'd be nice to use a try/always block to do the
job currently performed by a trap to unfunction compadd. So how do you
enable try and always but restore their original state when completion
exits. It's possible using the parameters module but would get fairly
messy. And we'd be wanting to re-disable `tried' from within an always
block.

Oliver


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

* Re: PATCH: `try' syntax
  2004-06-14 18:14       ` Bart Schaefer
  2004-06-15 10:31         ` Oliver Kiddle
@ 2004-06-15 10:51         ` DervishD
  2004-06-15 13:33         ` Peter Stephenson
  2 siblings, 0 replies; 13+ messages in thread
From: DervishD @ 2004-06-15 10:51 UTC (permalink / raw)
  To: Zsh hackers list

    Hi Bart :)

 * Bart Schaefer <schaefer@brasslantern.com> dixit:
> * How about making this work with any loop body by replacing "do" with
> "try"?  Then get rid of "try" as a standalone reserved word and instead
> use "repeat 1; try ... always ... done"

    That looks quite good to me. Makes the 'try' extension easier to
use, easier to integrate with existing scripts and a lot easier to
remember.
 
> * Rather than putting colons or some other unlikely character in front of
> the name, use plain words and start with them "disable"d, so that in order
> to use this syntax one must first "enable -r try always tried".  (This
> technique could apply to other extension syntax as well.)

    This looks good to me, too. I wouldn't disable it by default,
they shoudn't cause no harm. And if they do, how about 'ztry'?

    Raúl Núñez de Arenas Coronado

-- 
Linux Registered User 88736
http://www.pleyades.net & http://raul.pleyades.net/


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

* Re: PATCH: `try' syntax
  2004-06-14 18:14       ` Bart Schaefer
  2004-06-15 10:31         ` Oliver Kiddle
  2004-06-15 10:51         ` DervishD
@ 2004-06-15 13:33         ` Peter Stephenson
  2004-06-15 20:21           ` Bart Schaefer
  2 siblings, 1 reply; 13+ messages in thread
From: Peter Stephenson @ 2004-06-15 13:33 UTC (permalink / raw)
  To: Zsh hackers list

Bart Schaefer wrote:
> * A "shortloops" form "try { ... }" might be nice, or maybe should be the
> only form.

It's rather nonstandard.

> * How about making this work with any loop body by replacing "do" with
> "try"?  Then get rid of "try" as a standalone reserved word and instead
> use "repeat 1; try ... always ... done"

That's mixing up the syntax in exactly the way I wanted to avoid.
I wanted the `try' bit to be separate so that it was clear what
was going on.

> * Rather than putting colons or some other unlikely character in front of
> the name, use plain words and start with them "disable"d, so that in order
> to use this syntax one must first "enable -r try always tried".  (This
> technique could apply to other extension syntax as well.)

Yes, it's interesting, but the problem with functions is difficult.
An extra flag to autoload could specify extended syntax.

> * Tangential thought: Is it really necessary to disable e.g. both "case"
> and "esac" or is it sufficient to disable (and enable) "case"?

Currently you do need to disable both.

> So does that mean that
> 
>    :try
>      setopt errexit
>      false
>      print not reached 1
>    :always
>      print reached
>    :tried
>    print not reached 2
> 
> prints only "reached"?  Then that's a non-obvious way to accomplish what
> Oliver wanted:
> 
> > > It'd be really useful to have a way to skip over the rest of the try
> > > block, going straight to the always code.

You'd need to use errreturn, since exit is immediate.  Otherwise
it works.

Oliver wrote:
> Or, thinking along those lines, you could use something like
> { ... } always { ... }
> That currently finds a syntax error at always.

This is definitely an interesting suggestion...

Syntactically, it's still a bit tricky.  Either `always' is a keyword,
or it isn't.  If it is it generates a syntax error when used elsewhere.
If it isn't, we have to convert the string into a token in this one
case.  Maybe that's not so hard, though.

The other part is that we don't know till we get to the `always'
whether there is an always present.  Possibly it could just be
tacked onto ordinary current-shell structures.

As things stand I'm not sure I'm going to have any free time before
2008, but if I do is this worth trying?  (In other words, is it
reasonably agreeable to everyone interested?)

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 692070


**********************************************************************
This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the system manager.

This footnote also confirms that this email message has been swept by
MIMEsweeper for the presence of computer viruses.

www.mimesweeper.com
**********************************************************************


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

* Re: PATCH: `try' syntax
  2004-06-15 13:33         ` Peter Stephenson
@ 2004-06-15 20:21           ` Bart Schaefer
  2004-06-16 14:33             ` Oliver Kiddle
  0 siblings, 1 reply; 13+ messages in thread
From: Bart Schaefer @ 2004-06-15 20:21 UTC (permalink / raw)
  To: Zsh hackers list

On Tue, 15 Jun 2004, Oliver Kiddle wrote:

> Bart wrote:
> > * Rather than putting colons or some other unlikely character in front
> > of the name, use plain words and start with them "disable"d, so that
> > in order to use this syntax one must first "enable -r try always
> > tried".  (This technique could apply to other extension syntax as
> > well.)
> 
> That would be quite a good way of handling it. My main reservation
> concerns the fact that it isn't possible to make the enabled/disabled
> state of builtins local.

Hmm, good point.  You'd have to do something like

	enable try always tried
	try
          # ...
        always
	  disable try always tried
        tried

(which should work, because the entire "try ... tried" must be parsed
before the disable executes), but one would also have to store the initial
state of the keywords and then test whether to disable them.

> Consider _approximate. It'd be nice to use a try/always block to do the
> job currently performed by a trap to unfunction compadd. So how do you
> enable try and always but restore their original state when completion
> exits.

Maybe the "enable" command should have a -L option like "emulate", that
works like "setopt localoptions".  There could even be an option for it,
"localenable".  (This begs the question of whether "localdisable" is a
synonym or ...)

> It's possible using the parameters module but would get fairly messy.

Hmm ... wouldn't

	local -a +h reswords dis_reswords

cover all possible cases?  (Hmm, again; no, it seems it does not, because
either the values aren't restored and/or assigning to these arrays doesn't
accomplish enable/disable.  Odd, but oh, well.)

On Tue, 15 Jun 2004, Peter Stephenson wrote:

> Bart Schaefer wrote:
> > * A "shortloops" form "try { ... }" might be nice, or maybe should be the
> > only form.
> 
> It's rather nonstandard.

So is the whole "try" concept, at least for shells.

> Oliver wrote:
> > Or, thinking along those lines, you could use something like
> > { ... } always { ... }
> > That currently finds a syntax error at always.
> 
> Syntactically, it's still a bit tricky.  Either `always' is a keyword,
> or it isn't.

Not unprecedented; "in" behaves like a keyword in certain positions, but
is not one.

> The other part is that we don't know till we get to the `always'
> whether there is an always present.  Possibly it could just be
> tacked onto ordinary current-shell structures.

In the case of "in" there's always an introducer ("case", "for", etc.),
which would argue for leaving the "try" somewhere.

On a different tangent, maybe the whole thing should parse more like
"coproc" does, and perhaps "always" should be replaced with something
like the ";&" from "case".

> As things stand I'm not sure I'm going to have any free time before
> 2008, but if I do is this worth trying?  (In other words, is it
> reasonably agreeable to everyone interested?)

It's definitely getting close.


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

* Re: PATCH: `try' syntax
  2004-06-15 20:21           ` Bart Schaefer
@ 2004-06-16 14:33             ` Oliver Kiddle
  2004-06-16 17:21               ` Bart Schaefer
  0 siblings, 1 reply; 13+ messages in thread
From: Oliver Kiddle @ 2004-06-16 14:33 UTC (permalink / raw)
  To: Zsh hackers list

Bart wrote:

> Hmm ... wouldn't
> 
> 	local -a +h reswords dis_reswords
> 
> cover all possible cases?  (Hmm, again; no, it seems it does not, because
> either the values aren't restored and/or assigning to these arrays doesn't
> accomplish enable/disable.  Odd, but oh, well.)

It's not odd if you look briefly at how everything in the parameters
module is implemented. When a value is looked up it gets the
information from the internal structure and returns it. Making local
save and restore these internal structures would be fairly messy.
Ideally zsh would store all internal information directly in parameters
so local would work on them automatically. In the long term that'd be a
better thing to aim at than adding lots of local* options.

> On Tue, 15 Jun 2004, Peter Stephenson wrote:

> > As things stand I'm not sure I'm going to have any free time before
> > 2008, but if I do is this worth trying?  (In other words, is it
> > reasonably agreeable to everyone interested?)

I'm agreeable to it.

Oliver


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

* Re: PATCH: `try' syntax
  2004-06-16 14:33             ` Oliver Kiddle
@ 2004-06-16 17:21               ` Bart Schaefer
  2004-06-18 11:05                 ` PATCH: second go at `always' blocks Peter Stephenson
  0 siblings, 1 reply; 13+ messages in thread
From: Bart Schaefer @ 2004-06-16 17:21 UTC (permalink / raw)
  To: Zsh hackers list

On Wed, 16 Jun 2004, Oliver Kiddle wrote:

> Bart wrote:
> 
> > 	local -a +h reswords dis_reswords
> > 
> > either the values aren't restored and/or assigning to these arrays
> > doesn't accomplish enable/disable.
> 
> It's not odd if you look briefly at how everything in the parameters
> module is implemented. When a value is looked up it gets the
> information from the internal structure and returns it. Making local
> save and restore these internal structures would be fairly messy.

I'm pretty sure it works for _some_ variables; $functions in particular.

schaefer<501> x() {
> local -A +h functions
> print $#functions
> unfunction x
> print $#functions
> }
schaefer<502> x
502
501
schaefer<503> print $#functions
502
schaefer<504> x
502
501

Yep, works for $functions.  Maybe it's because $reswords is not a hash.
(Why did we go with two arrays rather than a hash with resword keys and
enabled/disabled values?  I've forgotten.)


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

* PATCH: second go at `always' blocks
  2004-06-16 17:21               ` Bart Schaefer
@ 2004-06-18 11:05                 ` Peter Stephenson
  0 siblings, 0 replies; 13+ messages in thread
From: Peter Stephenson @ 2004-06-18 11:05 UTC (permalink / raw)
  To: Zsh hackers list

Here is a second attempt following Oliver's and Bart's suggestions.

The new syntax is:

  {
    # execute normally
  } always {
    # execute always
    # optionally reset error
    (( TRY_BLOCK_ERROR = 0 ))
  }

This doesn't clash with any currently valid zsh syntax, except that
we don't have parameter namespaces so can't say .sh.try_block_error.

Please read the description in grammar.yo (zshmisc.1) and see if
I've made it clear.

There is no special syntax for aborting the `try' block (I've continued
to use that language for clarity even though there is no corresponding
keyword).

Index: Doc/Zsh/grammar.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/grammar.yo,v
retrieving revision 1.8
diff -u -r1.8 grammar.yo
--- Doc/Zsh/grammar.yo	24 Jul 2001 13:16:09 -0000	1.8
+++ Doc/Zsh/grammar.yo	18 Jun 2004 10:56:08 -0000
@@ -234,6 +234,63 @@
 item(tt({) var(list) tt(}))(
 Execute var(list).
 )
+findex(always)
+cindex(always blocks)
+cindex(try blocks)
+item(tt({) var(try-list) tt(} always {) var(always-list) tt(}))(
+First execute var(try-list).  Regardless of errors, or tt(break),
+tt(continue), or tt(return) commands encountered within var(try-list),
+execute var(always-list).  Execution then continues from the
+result of the execution of var(try-list); in other words, any error,
+or tt(break), tt(continue), or tt(return) command is treated in the
+normal way, as if var(always-list) were not present.  The two
+chunks of code are referred to as the `try block' and the `always block'.
+
+Optional newlines or semicolons may appear after the tt(always);
+note, however, that they may em(not) appear between the preceeding
+closing brace and the tt(always).
+
+An `error' in this context is a condition such as a syntax error which
+causes the shell to abort execution of the current function, script, or
+list.  Syntax errors encountered while the shell is parsing the
+code do not cause the var(always-list) to be executed.  For example,
+an erroneously constructed tt(if) block in tt(try-list) would cause the
+shell to abort during parsing, so that tt(always-list) would not be
+executed, while an erroneous substitution such as tt(${*foo*}) would
+cause a run-time error, after which tt(always-list) would be executed.
+
+An error condition can be tested and reset with the special integer
+variable tt(TRY_BLOCK_ERROR).  Outside an tt(always-list) the value is
+irrelevant, but it is initialised to tt(-1).  Inside tt(always-list), the
+value is 1 if an error occurred in the tt(try-list), else 0.  If
+tt(TRY_BLOCK_ERROR) is set to 0 during the tt(always-list), the error
+condition caused by the tt(try-list) is reset, and shell execution
+continues normally after the end of tt(always-list).  Altering the value
+during the tt(try-list) is not useful (unless this forms part of an
+enclosing tt(always) block).  Regardless of tt(TRY_BLOCK_ERROR), the
+normal shell status after the end of tt(always-list) is the value
+returned from tt(always-list).
+
+The following executes the given code, ignoring any errors it causes.
+This is an alternative to the usual convention of protecting code by
+executing it in a subshell.
+
+example({
+    # code which may cause an error
+  } always {
+    # This code is executed regardless of the error.
+    (( TRY_BLOCK_ERROR = 0 ))
+}
+# The error condition has been reset.)
+
+Note that the return status tt($?) after the end of the tt(always) block
+reflects the return status at the end of the var(try-list), consistent
+with other parts of the shell status.
+
+An tt(exit) command encountered in tt(try-list) does em(not) cause the
+execution of var(always-list).  Instead, the shell exits immediately
+after any tt(EXIT) trap has been executed.
+)
 findex(function)
 xitem(tt(function) var(word) ... [ tt(()) ] [ var(term) ] tt({) var(list) tt(}))
 xitem(var(word) ... tt(()) [ var(term) ] tt({) var(list) tt(}))
Index: Doc/Zsh/params.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/params.yo,v
retrieving revision 1.21
diff -u -r1.21 params.yo
--- Doc/Zsh/params.yo	6 Apr 2004 13:01:09 -0000	1.21
+++ Doc/Zsh/params.yo	18 Jun 2004 10:56:09 -0000
@@ -619,6 +619,14 @@
 item(tt(signals))(
 An array containing the names of the signals.
 )
+vindex(TRY_BLOCK_ERROR)
+item(tt(TRY_BLOCK_ERROR) <S>)(
+In an tt(always) block, indicates whether the preceding list of code
+caused an error.  The value is 1 to indicate an error, 0 otherwise.
+It may be reset, clearing the error condition.  See
+ifzman(em(Complex Commands) in zmanref(zshmisc))\
+ifnzman(noderef(Complex Commands))
+)
 vindex(TTY)
 item(tt(TTY))(
 The name of the tty associated with the shell, if any.
Index: Functions/MIME/zsh-mime-setup
===================================================================
RCS file: /cvsroot/zsh/zsh/Functions/MIME/zsh-mime-setup,v
retrieving revision 1.1
diff -u -r1.1 zsh-mime-setup
--- Functions/MIME/zsh-mime-setup	14 Sep 2003 19:37:35 -0000	1.1
+++ Functions/MIME/zsh-mime-setup	18 Jun 2004 10:56:09 -0000
@@ -62,9 +62,8 @@
 zstyle -a :mime: mailcap cap_files ||
   cap_files=(~/.mailcap /etc/mailcap)
 
-TRAPEXIT() { unfunction mime-setup-add-type >&/dev/null; return 0; }
-
-mime-setup-add-type() {
+{
+  mime-setup-add-type() {
     local type suffix
     local -a array
 
@@ -90,115 +89,117 @@
 	    fi
 	fi
     done
-}
+  }
 
-# Loop through files to find suffixes for MIME types.
-# Earlier entries take precedence, so the files need to be listed
-# with the user's own first.  This also means pre-existing
-# values in suffix_type_map are respected.
-for file in $type_files; do
+  # Loop through files to find suffixes for MIME types.
+  # Earlier entries take precedence, so the files need to be listed
+  # with the user's own first.  This also means pre-existing
+  # values in suffix_type_map are respected.
+  for file in $type_files; do
     [[ -r $file ]] || continue
 
     # For once we rely on the fact that read handles continuation
     # lines ending in backslashes, i.e. there's no -r.
     while read line; do
-	# Skip blank or comment lines.
-	[[ $line = [[:space:]]#(\#*|) ]] && continue
-
-	# There are two types of line you find in MIME type files.
-	# The original simple sort contains the type name then suffixes
-	# separated by whitespace.  However, Netscape insists
-	# on adding lines with backslash continuation with
-	# key="value" pairs.  So we'd better handle both.
-	if [[ $line = *=* ]]; then
-	    # Gory.
-	    # This relies on the fact that a typical entry:
-	    #   type=video/x-mpeg2 desc="MPEG2 Video" exts="mpv2,mp2v"
-	    # looks like a parameter assignment.  However, we really
-	    # don't want to be screwed up by future extensions,
-	    # so we split the elements to an array and pick out the
-	    # ones we're interested in.
-	    type= exts=
-
-	    # Syntactically split line to preserve quoted words.
-	    array=(${(z)line})
-	    for elt in $array; do
-		if [[ $elt = (type|exts)=* ]]; then
-		    eval $elt
-		fi
-	    done
+      # Skip blank or comment lines.
+      [[ $line = [[:space:]]#(\#*|) ]] && continue
 
-	    # Get extensions by splitting on comma
-	    array=(${(s.,.)exts})
-
-	    [[ -n $type ]] && mime-setup-add-type $type $array
-	else
-	    # Simple.
-	    mime-setup-add-type ${=line}
-	fi
+      # There are two types of line you find in MIME type files.
+      # The original simple sort contains the type name then suffixes
+      # separated by whitespace.  However, Netscape insists
+      # on adding lines with backslash continuation with
+      # key="value" pairs.  So we'd better handle both.
+      if [[ $line = *=* ]]; then
+        # Gory.
+        # This relies on the fact that a typical entry:
+        #   type=video/x-mpeg2 desc="MPEG2 Video" exts="mpv2,mp2v"
+        # looks like a parameter assignment.  However, we really
+        # don't want to be screwed up by future extensions,
+        # so we split the elements to an array and pick out the
+        # ones we're interested in.
+        type= exts=
+
+        # Syntactically split line to preserve quoted words.
+        array=(${(z)line})
+        for elt in $array; do
+          if [[ $elt = (type|exts)=* ]]; then
+            eval $elt
+          fi
+        done
+
+        # Get extensions by splitting on comma
+        array=(${(s.,.)exts})
+
+        [[ -n $type ]] && mime-setup-add-type $type $array
+      else
+        # Simple.
+        mime-setup-add-type ${=line}
+      fi
     done <$file
-done
-
+  done
+} always {
+  unfunction mime-setup-add-type >&/dev/null
+}
 
 # Loop through files to find handlers for types.
 for file in $cap_files; do
-    [[ -r $file ]] || continue
+  [[ -r $file ]] || continue
 
-    # Oh, great.  We need to preserve backslashes inside the line,
-    # but need to manage continuation lines.
-    while read -r line; do
-	# Skip blank or comment lines.
-	[[ $line = [[:space:]]#(\#*|) ]] && continue
-
-	while [[ $line = (#b)(*)\\ ]]; do
-	    line=$match[1]
-	    read -r line2 || break
-	    line+=$line2
-	done
-
-	# Guess what, this file has a completely different format.
-	# See mailcap(4).
-	# The biggest unpleasantness here is that the fields are
-	# delimited by semicolons, but the command field, which
-	# is the one we want to extract, may itself contain backslashed
-	# semicolons.
-	if [[ $line = (#b)[[:space:]]#([^[:space:]\;]##)[[:space:]]#\;(*) ]]
-	then
-	    # this is the only form we can handle, but there's no point
-	    # issuing a warning for other forms.
-	    type=$match[1]
-            line=$match[2]
-	    # See if it has flags after the command.
-	    if [[ $line = (#b)(([^\;\\]|\\\;|\\[^\;])#)\;(*) ]]; then
-		line=$match[1]
-		flags=$match[3]
-	    else
-		flags=
-	    fi
-	    # Remove quotes from semicolons
-	    line=${line//\\\;/\;}
-	    # and remove any surrounding white space --- this might
-	    # make the handler empty.
-	    line=${${line##[[:space:]]#}%%[[:space:]]}
-	    if [[ -z $type_handler_map[$type] ]]; then
-		if [[ -n $o_verbose ]]; then
-		    print -r "Adding handler for type $type:
+  # Oh, great.  We need to preserve backslashes inside the line,
+  # but need to manage continuation lines.
+  while read -r line; do
+    # Skip blank or comment lines.
+    [[ $line = [[:space:]]#(\#*|) ]] && continue
+
+    while [[ $line = (#b)(*)\\ ]]; do
+      line=$match[1]
+      read -r line2 || break
+      line+=$line2
+    done
+
+    # Guess what, this file has a completely different format.
+    # See mailcap(4).
+    # The biggest unpleasantness here is that the fields are
+    # delimited by semicolons, but the command field, which
+    # is the one we want to extract, may itself contain backslashed
+    # semicolons.
+    if [[ $line = (#b)[[:space:]]#([^[:space:]\;]##)[[:space:]]#\;(*) ]]
+    then
+      # this is the only form we can handle, but there's no point
+      # issuing a warning for other forms.
+      type=$match[1]
+      line=$match[2]
+      # See if it has flags after the command.
+      if [[ $line = (#b)(([^\;\\]|\\\;|\\[^\;])#)\;(*) ]]; then
+        line=$match[1]
+        flags=$match[3]
+      else
+        flags=
+      fi
+      # Remove quotes from semicolons
+      line=${line//\\\;/\;}
+      # and remove any surrounding white space --- this might
+      # make the handler empty.
+      line=${${line##[[:space:]]#}%%[[:space:]]}
+      if [[ -z $type_handler_map[$type] ]]; then
+        if [[ -n $o_verbose ]]; then
+          print -r "Adding handler for type $type:
   $line" >&2
-		fi
-		type_handler_map[$type]=$line
-		type_flags_map[$type]=$flags
-		if [[ -n $flags && -n $o_verbose ]]; then
-		    print -r "  with flags $flags" >&2
-		fi
-	    elif [[ -n $o_verbose ]]; then
-		print -r "Skipping handler for already defined type $type:
+	fi
+	type_handler_map[$type]=$line
+	type_flags_map[$type]=$flags
+	if [[ -n $flags && -n $o_verbose ]]; then
+	  print -r "  with flags $flags" >&2
+	fi
+      elif [[ -n $o_verbose ]]; then
+	print -r "Skipping handler for already defined type $type:
   $line" >&2
-		if [[ -n $flags ]]; then
-		    print -r " with flags $flags" >&2
-		fi
-	    fi
+	if [[ -n $flags ]]; then
+	  print -r " with flags $flags" >&2
 	fi
-    done <$file
+      fi
+    fi
+  done <$file
 done
 
 
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.64
diff -u -r1.64 exec.c
--- Src/exec.c	2 Jun 2004 22:14:25 -0000	1.64
+++ Src/exec.c	18 Jun 2004 10:56:16 -0000
@@ -137,10 +137,10 @@
 
 /* Execution functions. */
 
-static int (*execfuncs[]) _((Estate, int)) = {
+static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
     execcursh, exectime, execfuncdef, execfor, execselect,
     execwhile, execrepeat, execcase, execif, execcond,
-    execarith, execautofn
+    execarith, execautofn, exectry
 };
 
 /* structure for command builtin for when it is used with -v or -V */
@@ -325,6 +325,9 @@
 {
     Wordcode end = state->pc + WC_CURSH_SKIP(state->pc[-1]);
 
+    /* Skip word only used for try/always */
+    state->pc++;
+
     if (!list_pipe && thisjob != list_pipe_job && !hasprocs(thisjob))
 	deletejob(jobtab + thisjob);
     cmdpush(CS_CURSH);
@@ -2475,6 +2478,9 @@
                 subsh_close = -1;
 		/* If we're forked (and we should be), no need to return */
 		DPUTS(last1 != 1 && !forked, "BUG: not exiting?");
+		DPUTS(type != WC_SUBSH, "Not sure what we're doing.");
+		/* Skip word only used for try/always blocks */
+		state->pc++;
 		execlist(state, 0, 1);
 	    }
 	}
Index: Src/loop.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/loop.c,v
retrieving revision 1.13
diff -u -r1.13 loop.c
--- Src/loop.c	2 Jun 2004 22:14:26 -0000	1.13
+++ Src/loop.c	18 Jun 2004 10:56:17 -0000
@@ -616,3 +616,68 @@
 
     return lastval;
 }
+
+/*
+ * Errflag from `try' block, may be reset in `always' block.
+ * Accessible from an integer parameter, so needs to be a zlong.
+ */
+
+/**/
+zlong
+try_errflag = -1;
+
+/**/
+int
+exectry(Estate state, int do_exec)
+{
+    Wordcode end, always;
+    int endval;
+    int save_retflag, save_breaks, save_loops, save_contflag;
+    zlong save_try_errflag;
+
+    end = state->pc + WC_TRY_SKIP(state->pc[-1]);
+    always = state->pc + 1 + WC_TRY_SKIP(*state->pc);
+    state->pc++;
+    pushheap();
+    cmdpush(CS_CURSH);
+
+    /* The :try clause */
+    execlist(state, 1, do_exec);
+
+    /* Don't record errflag here, may be reset. */
+    endval = lastval;
+
+    freeheap();
+
+    cmdpop();
+    cmdpush(CS_ALWAYS);
+
+    /* The always clause. */
+    save_try_errflag = try_errflag;
+    try_errflag = (zlong)errflag;
+    errflag = 0;
+    save_retflag = retflag;
+    retflag = 0;
+    save_breaks = breaks;
+    breaks = 0;
+    save_loops = loops;
+    loops = 0;
+    save_contflag = contflag;
+    contflag = 0;
+
+    state->pc = always;
+    execlist(state, 1, do_exec);
+
+    errflag = try_errflag ? 1 : 0;
+    try_errflag = save_try_errflag;
+    retflag = save_retflag;
+    breaks = save_breaks;
+    loops = save_loops;
+    contflag = save_contflag;
+
+    cmdpop();
+    popheap();
+    state->pc = end;
+
+    return endval;
+}
Index: Src/params.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/params.c,v
retrieving revision 1.85
diff -u -r1.85 params.c
--- Src/params.c	2 Jun 2004 22:14:26 -0000	1.85
+++ Src/params.c	18 Jun 2004 10:56:19 -0000
@@ -106,6 +106,7 @@
  
 /* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */
 
+
 /**/
 mod_export int termflags;
  
@@ -191,6 +192,7 @@
 IPDEF5("LINES", &lines, zlevarsetfn),
 IPDEF5("OPTIND", &zoptind, intvarsetfn),
 IPDEF5("SHLVL", &shlvl, intvarsetfn),
+IPDEF5("TRY_BLOCK_ERROR", &try_errflag, intvarsetfn),
 
 #define IPDEF7(A,B) {NULL,A,PM_SCALAR|PM_SPECIAL,BR((void *)B),SFN(strvarsetfn),GFN(strvargetfn),stdunsetfn,0,NULL,NULL,NULL,0}
 IPDEF7("OPTARG", &zoptarg),
Index: Src/parse.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/parse.c,v
retrieving revision 1.44
diff -u -r1.44 parse.c
--- Src/parse.c	2 Jun 2004 22:14:26 -0000	1.44
+++ Src/parse.c	18 Jun 2004 10:56:20 -0000
@@ -1330,25 +1330,55 @@
 }
 
 /*
- * subsh	: ( INPAR | INBRACE ) list ( OUTPAR | OUTBRACE )
+ * subsh	: INPAR list OUTPAR |
+ *                INBRACE list OUTBRACE [ "always" INBRACE list OUTBRACE ]
  */
 
 /**/
 static void
 par_subsh(int *complex)
 {
-    int oecused = ecused, otok = tok, p;
+    int oecused = ecused, otok = tok, p, pp;
 
     p = ecadd(0);
+    /* Extra word only needed for always block */
+    pp = ecadd(0);
     yylex();
     par_list(complex);
     ecadd(WCB_END());
     if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE))
 	YYERRORV(oecused);
-    ecbuf[p] = (otok == INPAR ? WCB_SUBSH(ecused - 1 - p) :
-		WCB_CURSH(ecused - 1 - p));
     incmdpos = 1;
     yylex();
+
+    /* Optional always block.  No intervening SEPERs allowed. */
+    if (otok == INBRACE && tok == STRING && !strcmp(tokstr, "always")) {
+	ecbuf[pp] = WCB_TRY(ecused - 1 - pp);
+	incmdpos = 1;
+	do {
+	    yylex();
+	} while (tok == SEPER);
+
+	if (tok != INBRACE)
+	    YYERRORV(oecused);
+	cmdpop();
+	cmdpush(CS_ALWAYS);
+
+	yylex();
+	par_save_list(complex);
+	while (tok == SEPER)
+	    yylex();
+
+	incmdpos = 1;
+
+	if (tok != OUTBRACE)
+	    YYERRORV(oecused);
+	yylex();
+	ecbuf[p] = WCB_TRY(ecused - 1 - p);
+    } else {
+	ecbuf[p] = (otok == INPAR ? WCB_SUBSH(ecused - 1 - p) :
+		    WCB_CURSH(ecused - 1 - p));
+    }
 }
 
 /*
Index: Src/prompt.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/prompt.c,v
retrieving revision 1.18
diff -u -r1.18 prompt.c
--- Src/prompt.c	28 May 2004 19:21:46 -0000	1.18
+++ Src/prompt.c	18 Jun 2004 10:56:21 -0000
@@ -49,7 +49,7 @@
 
 /* parser states, for %_ */
 
-static char *cmdnames[] = {
+static char *cmdnames[CS_COUNT] = {
     "for",      "while",     "repeat",    "select",
     "until",    "if",        "then",      "else",
     "elif",     "math",      "cond",      "cmdor",
@@ -57,7 +57,7 @@
     "case",     "function",  "subsh",     "cursh",
     "array",    "quote",     "dquote",    "bquote",
     "cmdsubst", "mathsubst", "elif-then", "heredoc",
-    "heredocd", "brace",     "braceparam",
+    "heredocd", "brace",     "braceparam", "always",
 };
  
 /* The buffer into which an expanded and metafied prompt is being written, *
Index: Src/text.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/text.c,v
retrieving revision 1.13
diff -u -r1.13 text.c
--- Src/text.c	4 Feb 2003 11:23:05 -0000	1.13
+++ Src/text.c	18 Jun 2004 10:56:21 -0000
@@ -350,6 +350,8 @@
 		taddnl();
 		n = tpush(code, 1);
 		n->u._subsh.end = state->pc + WC_SUBSH_SKIP(code);
+		/* skip word only use for try/always */
+		state->pc++;
 	    } else {
 		state->pc = s->u._subsh.end;
 		tindent--;
@@ -365,6 +367,8 @@
 		taddnl();
 		n = tpush(code, 1);
 		n->u._subsh.end = state->pc + WC_CURSH_SKIP(code);
+		/* skip word only use for try/always */
+		state->pc++;
 	    } else {
 		state->pc = s->u._subsh.end;
 		tindent--;
@@ -721,6 +725,30 @@
 	    taddstr("))");
 	    stack = 1;
 	    break;
+	case WC_TRY:
+	    if (!s) {
+		taddstr("{");
+		tindent++;
+		taddnl();
+		n = tpush(code, 0);
+		state->pc++;
+		/* this is the end of the try block alone */
+		n->u._subsh.end = state->pc + WC_CURSH_SKIP(state->pc[-1]);
+	    } else if (!s->pop) {
+		state->pc = s->u._subsh.end;
+		tindent--;
+		taddnl();
+		taddstr("} always {");
+		tindent++;
+		taddnl();
+		s->pop = 1;
+	    } else {
+		tindent--;
+		taddnl();
+		taddstr("}");
+		stack = 1;
+	    }
+	    break;
 	case WC_END:
 	    stack = 1;
 	    break;
Index: Src/zsh.h
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v
retrieving revision 1.56
diff -u -r1.56 zsh.h
--- Src/zsh.h	20 Apr 2004 12:11:16 -0000	1.56
+++ Src/zsh.h	18 Jun 2004 10:56:22 -0000
@@ -580,6 +580,10 @@
 #define WC_COND    17
 #define WC_ARITH   18
 #define WC_AUTOFN  19
+#define WC_TRY     20
+
+/* increment as necessary */
+#define WC_COUNT   21
 
 #define WCB_END()           wc_bld(WC_END, 0)
 
@@ -657,6 +661,9 @@
 #define WC_REPEAT_SKIP(C)   wc_data(C)
 #define WCB_REPEAT(O)       wc_bld(WC_REPEAT, (O))
 
+#define WC_TRY_SKIP(C)	    wc_data(C)
+#define WCB_TRY(O)	    wc_bld(WC_TRY, (O))
+
 #define WC_CASE_TYPE(C)     (wc_data(C) & 3)
 #define WC_CASE_HEAD        0
 #define WC_CASE_OR          1
@@ -1695,6 +1702,10 @@
 #define CS_HEREDOCD    28
 #define CS_BRACE       29
 #define CS_BRACEPAR    30
+#define CS_ALWAYS      31
+
+/* Increment as necessary */
+#define CS_COUNT       32
 
 /*********************
  * Memory management *

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 692070


**********************************************************************
This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the system manager.

This footnote also confirms that this email message has been swept by
MIMEsweeper for the presence of computer viruses.

www.mimesweeper.com
**********************************************************************


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

end of thread, other threads:[~2004-06-18 11:06 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-06-14 11:23 PATCH: `try' syntax Peter Stephenson
2004-06-14 12:58 ` Oliver Kiddle
2004-06-14 13:52   ` Peter Stephenson
2004-06-14 16:55     ` Oliver Kiddle
2004-06-14 17:27       ` Peter Stephenson
2004-06-14 18:14       ` Bart Schaefer
2004-06-15 10:31         ` Oliver Kiddle
2004-06-15 10:51         ` DervishD
2004-06-15 13:33         ` Peter Stephenson
2004-06-15 20:21           ` Bart Schaefer
2004-06-16 14:33             ` Oliver Kiddle
2004-06-16 17:21               ` Bart Schaefer
2004-06-18 11:05                 ` PATCH: second go at `always' blocks Peter Stephenson

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