From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 14830 invoked from network); 14 Jun 2004 11:24:49 -0000 Received: from thor.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.86) by ns1.primenet.com.au with SMTP; 14 Jun 2004 11:24:49 -0000 Received: (qmail 4685 invoked from network); 14 Jun 2004 11:24:20 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 14 Jun 2004 11:24:20 -0000 Received: (qmail 18134 invoked by alias); 14 Jun 2004 11:24:15 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 20036 Received: (qmail 18121 invoked from network); 14 Jun 2004 11:24:14 -0000 Received: from thor.dotsrc.org (HELO a.mx.sunsite.dk) (qmailr@130.225.247.86) by sunsite.dk with SMTP; 14 Jun 2004 11:24:11 -0000 Received: (qmail 4473 invoked from network); 14 Jun 2004 11:24:11 -0000 Received: from lhuumrelay3.lnd.ops.eu.uu.net (62.189.58.19) by a.mx.sunsite.dk with SMTP; 14 Jun 2004 11:24:08 -0000 Received: from MAILSWEEPER01.csr.com (mailhost1.csr.com [62.189.183.235]) by lhuumrelay3.lnd.ops.eu.uu.net (8.11.0/8.11.0) with ESMTP id i5EBNfv09317 for ; Mon, 14 Jun 2004 11:23:41 GMT Received: from EXCHANGE02.csr.com (unverified [192.168.137.45]) by MAILSWEEPER01.csr.com (Content Technologies SMTPRS 4.3.12) with ESMTP id for ; Mon, 14 Jun 2004 12:23:03 +0100 Received: from news01.csr.com ([192.168.143.38]) by EXCHANGE02.csr.com with Microsoft SMTPSVC(5.0.2195.6713); Mon, 14 Jun 2004 12:26:46 +0100 Received: from news01.csr.com (localhost.localdomain [127.0.0.1]) by news01.csr.com (8.12.11/8.12.11) with ESMTP id i5EBNemc012082 for ; Mon, 14 Jun 2004 12:23:40 +0100 Received: from csr.com (pws@localhost) by news01.csr.com (8.12.11/8.12.11/Submit) with ESMTP id i5EBNdoM012078 for ; Mon, 14 Jun 2004 12:23:39 +0100 Message-Id: <200406141123.i5EBNdoM012078@news01.csr.com> X-Authentication-Warning: news01.csr.com: pws owned process doing -bs To: zsh-workers@sunsite.dk (Zsh hackers list) Subject: PATCH: `try' syntax Date: Mon, 14 Jun 2004 12:23:38 +0100 From: Peter Stephenson X-OriginalArrivalTime: 14 Jun 2004 11:26:46.0164 (UTC) FILETIME=[79E46540:01C45202] X-Spam-Checker-Version: SpamAssassin 2.63 on a.mx.sunsite.dk X-Spam-Level: X-Spam-Status: No, hits=0.0 required=6.0 tests=none autolearn=no version=2.63 X-Spam-Hits: 0.0 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 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 **********************************************************************