From e43b2acd67a7b06d4653ff910af1b4b51aedcdfe Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 1 Apr 2005 10:30:08 +0000 Subject: 21078: parse errors didn't cause non-zero exit status --- ChangeLog | 5 + Src/init.c | 407 +++++++++++++++++++++++++++++++++++----------- Test/A01grammar.ztst | 450 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 767 insertions(+), 95 deletions(-) create mode 100644 Test/A01grammar.ztst diff --git a/ChangeLog b/ChangeLog index 171c41bbf..470e10c3a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-04-01 Peter Stephenson + + * 21078: Src/init.c, Test/A01grammar.ztst: parse errors didn't + cause non-zero exit status. + 2005-03-27 Clint Adams * 21075: Completion/Unix/Command/_baz: update baz completion diff --git a/Src/init.c b/Src/init.c index e7dec681e..4fdf38de7 100644 --- a/Src/init.c +++ b/Src/init.c @@ -34,6 +34,8 @@ #include "init.pro" +#include "version.h" + /**/ int noexitct = 0; @@ -105,7 +107,8 @@ loop(int toplevel, int justonce) pushheap(); for (;;) { freeheap(); - errflag = 0; + if (stophist == 3) /* re-entry via preprompt() */ + hend(NULL); hbegin(1); /* init history mech */ if (isset(SHINSTDIN)) { setblock_stdin(); @@ -113,43 +116,55 @@ loop(int toplevel, int justonce) int hstop = stophist; stophist = 3; preprompt(); - stophist = hstop; + if (stophist != 3) + hbegin(1); + else + stophist = hstop; + errflag = 0; } } intr(); /* interrupts on */ lexinit(); /* initialize lexical state */ if (!(prog = parse_event())) { /* if we couldn't parse a list */ - hend(); + hend(NULL); if ((tok == ENDINPUT && !errflag) || (tok == LEXERR && (!isset(SHINSTDIN) || !toplevel)) || justonce) break; + if (tok == LEXERR && !lastval) + lastval = 1; continue; } - if (hend()) { + if (hend(prog)) { int toksav = tok; Eprog preprog; if (toplevel && (preprog = getshfunc("preexec")) != &dummy_eprog) { LinkList args; int osc = sfcontext; + char *cmdstr; args = znewlinklist(); zaddlinknode(args, "preexec"); - if (hist_ring) + /* If curline got dumped from the history, we don't know + * what the user typed. */ + if (hist_ring && curline.histnum == curhist) zaddlinknode(args, hist_ring->text); + else + zaddlinknode(args, ""); + zaddlinknode(args, getjobtext(prog, NULL)); + zaddlinknode(args, cmdstr = getpermtext(prog, NULL)); sfcontext = SFC_HOOK; doshfunc("preexec", preprog, args, 0, 1); sfcontext = osc; + zsfree(cmdstr); freelinklist(args, (FreeFunc) NULL); errflag = 0; } if (stopmsg) /* unset 'you have stopped jobs' flag */ stopmsg--; execode(prog, 0, 0); - if (toplevel) - freeeprogs(); tok = toksav; if (toplevel) noexitct = 0; @@ -184,10 +199,10 @@ static int restricted; void parseargs(char **argv) { + int optionbreak = 0; char **x; int action, optno; LinkList paramlist; - int bourne = (emulation == EMULATE_KSH || emulation == EMULATE_SH); argzero = *argv++; SHIN = 0; @@ -204,24 +219,47 @@ parseargs(char **argv) opts[SINGLECOMMAND] = 0; /* loop through command line options (begins with "-" or "+") */ - while (*argv && (**argv == '-' || **argv == '+')) { + while (!optionbreak && *argv && (**argv == '-' || **argv == '+')) { char *args = *argv; action = (**argv == '-'); - if(!argv[0][1]) + if (!argv[0][1]) *argv = "--"; while (*++*argv) { - /* The pseudo-option `--' signifies the end of options. * - * `-b' does too, csh-style, unless we're emulating a * - * Bourne style shell. */ - if (**argv == '-' || (!bourne && **argv == 'b')) { - argv++; - goto doneoptions; + if (**argv == '-') { + if(!argv[0][1]) { + /* The pseudo-option `--' signifies the end of options. */ + argv++; + goto doneoptions; + } + if(*argv != args+1 || **argv != '-') + goto badoptionstring; + /* GNU-style long options */ + ++*argv; + if (!strcmp(*argv, "version")) { + printf("zsh %s (%s-%s-%s)\n", + ZSH_VERSION, MACHTYPE, VENDOR, OSTYPE); + exit(0); + } + if (!strcmp(*argv, "help")) { + printhelp(); + exit(0); + } + /* `-' characters are allowed in long options */ + for(args = *argv; *args; args++) + if(*args == '-') + *args = '_'; + goto longoptions; } - if (**argv == 'c') { /* -c command */ + if (unset(SHOPTIONLETTERS) && **argv == 'b') { + /* -b ends options at the end of this argument */ + optionbreak = 1; + } else if (**argv == 'c') { + /* -c command */ cmd = *argv; opts[INTERACTIVE] &= 1; opts[SHINSTDIN] = 0; + scriptname = ztrdup("zsh"); } else if (**argv == 'o') { if (!*++*argv) argv++; @@ -229,9 +267,11 @@ parseargs(char **argv) zerr("string expected after -o", NULL, 0); exit(1); } - if(!(optno = optlookup(*argv))) + longoptions: + if (!(optno = optlookup(*argv))) { zerr("no such option: %s", *argv, 0); - else if (optno == RESTRICTED) + exit(1); + } else if (optno == RESTRICTED) restricted = action; else dosetopt(optno, action, 1); @@ -240,6 +280,7 @@ parseargs(char **argv) /* zsh's typtab not yet set, have to use ctype */ while (*++*argv) if (!isspace(STOUC(**argv))) { + badoptionstring: zerr("bad option string: `%s'", args, 0); exit(1); } @@ -284,19 +325,37 @@ parseargs(char **argv) if(isset(SINGLECOMMAND)) opts[INTERACTIVE] &= 1; opts[INTERACTIVE] = !!opts[INTERACTIVE]; - pparams = x = (char **) zcalloc((countlinknodes(paramlist) + 1) * sizeof(char *)); + pparams = x = (char **) zshcalloc((countlinknodes(paramlist) + 1) * sizeof(char *)); while ((*x++ = (char *)getlinknode(paramlist))); free(paramlist); argzero = ztrdup(argzero); } +/**/ +static void +printhelp(void) +{ + printf("Usage: %s [] [ ...]\n", argzero); + printf("\nSpecial options:\n"); + printf(" --help show this message, then exit\n"); + printf(" --version show zsh version number, then exit\n"); + if(unset(SHOPTIONLETTERS)) + printf(" -b end option processing, like --\n"); + printf(" -c take first argument as a command to execute\n"); + printf(" -o OPTION set an option by name (see below)\n"); + printf("\nNormal options are named. An option may be turned on by\n"); + printf("`-o OPTION', `--OPTION', `+o no_OPTION' or `+-no-OPTION'. An\n"); + printf("option may be turned off by `-o no_OPTION', `--no-OPTION',\n"); + printf("`+o OPTION' or `+-OPTION'. Options are listed below only in\n"); + printf("`--OPTION' or `--no-OPTION' form.\n"); + printoptionlist(); +} /**/ mod_export void init_io(void) { - long ttpgrp; static char outbuf[BUFSIZ], errbuf[BUFSIZ]; #ifdef RSH_BUG_WORKAROUND @@ -322,7 +381,13 @@ init_io(void) #endif if (shout) { - fclose(shout); + /* + * Check if shout was set to stderr, if so don't close it. + * We do this if we are interactive but don't have a + * terminal. + */ + if (shout != stderr) + fclose(shout); shout = 0; } if (SHTTY != -1) { @@ -391,29 +456,21 @@ init_io(void) /* We will only use zle if shell is interactive, * * SHTTY != -1, and shout != 0 */ - if (interact && SHTTY != -1) { + if (interact) { init_shout(); - if(!shout) + if(!SHTTY || !shout) opts[USEZLE] = 0; } else opts[USEZLE] = 0; #ifdef JOB_CONTROL - /* If interactive, make the shell the foreground process */ + /* If interactive, make sure the shell is in the foreground and is the + * process group leader. + */ + mypid = (zlong)getpid(); if (opts[MONITOR] && interact && (SHTTY != -1)) { - if ((mypgrp = GETPGRP()) > 0) { - while ((ttpgrp = gettygrp()) != -1 && ttpgrp != mypgrp) { - sleep(1); /* give parent time to change pgrp */ - mypgrp = GETPGRP(); - if (mypgrp == mypid) - attachtty(mypgrp); - if (mypgrp == gettygrp()) - break; - killpg(mypgrp, SIGTTIN); - mypgrp = GETPGRP(); - } - } else - opts[MONITOR] = 0; + origpgrp = GETPGRP(); + acquire_pgrp(); /* might also clear opts[MONITOR] */ } else opts[MONITOR] = 0; #else @@ -427,8 +484,18 @@ init_shout(void) { static char shoutbuf[BUFSIZ]; #if defined(JOB_CONTROL) && defined(TIOCSETD) && defined(NTTYDISC) - int ldisc = NTTYDISC; + int ldisc; +#endif + + if (SHTTY == -1) + { + /* Since we're interative, it's nice to have somewhere to write. */ + shout = stderr; + return; + } +#if defined(JOB_CONTROL) && defined(TIOCSETD) && defined(NTTYDISC) + ldisc = NTTYDISC; ioctl(SHTTY, TIOCSETD, (char *)&ldisc); #endif @@ -450,7 +517,8 @@ init_shout(void) static char *tccapnams[TC_COUNT] = { "cl", "le", "LE", "nd", "RI", "up", "UP", "do", "DO", "dc", "DC", "ic", "IC", "cd", "ce", "al", "dl", "ta", - "md", "so", "us", "me", "se", "ue", "ch" + "md", "so", "us", "me", "se", "ue", "ch", + "ku", "kd", "kl", "kr" }; /* Initialise termcap */ @@ -474,13 +542,13 @@ init_term(void) #ifdef TGETENT_ACCEPTS_NULL /* If possible, we let tgetent allocate its own termcap buffer */ - if (tgetent(NULL, term) != 1) { + if (tgetent(NULL, term) != TGETENT_SUCCESS) #else - if (tgetent(termbuf, term) != 1) { + if (tgetent(termbuf, term) != TGETENT_SUCCESS) #endif - + { if (isset(INTERACTIVE)) - zerr("can't find termcap info for %s", term, 0); + zerr("can't find terminal definition for %s", term, 0); errflag = 0; termflags |= TERM_BAD; return 0; @@ -552,14 +620,11 @@ setupvals(void) #endif struct timezone dummy_tz; char *ptr; -#ifdef HAVE_GETRLIMIT - int i; -#endif + int i, j; #if defined(SITEFPATH_DIR) || defined(FPATH_DIR) char **fpathptr; # if defined(FPATH_DIR) && defined(FPATH_SUBDIRS) char *fpath_subdirs[] = FPATH_SUBDIRS; - int j; # endif # ifdef SITEFPATH_DIR int fpathlen = 1; @@ -567,6 +632,47 @@ setupvals(void) int fpathlen = 0; # endif #endif + int close_fds[10], tmppipe[2]; + + /* + * Workaround a problem with NIS (in one guise or another) which + * grabs file descriptors and keeps them for future reference. + * We don't want these to be in the range where the user can + * open fd's, i.e. 0 to 9 inclusive. So we make sure all + * fd's in that range are in use. + */ + memset(close_fds, 0, 10*sizeof(int)); + if (pipe(tmppipe) == 0) { + /* + * Strategy: Make sure we have at least fd 0 open (hence + * the pipe). From then on, keep dup'ing until we are + * up to 9. If we go over the top, close immediately, else + * mark for later closure. + */ + i = -1; /* max fd we have checked */ + while (i < 9) { + /* j is current fd */ + if (i < tmppipe[0]) + j = tmppipe[0]; + else if (i < tmppipe[1]) + j = tmppipe[1]; + else { + j = dup(0); + if (j == -1) + break; + } + if (j < 10) + close_fds[j] = 1; + else + close(j); + if (i < j) + i = j; + } + if (i < tmppipe[0]) + close(tmppipe[0]); + if (i < tmppipe[1]) + close(tmppipe[1]); + } addhookdefs(argzero, zshhooks, sizeof(zshhooks)/sizeof(*zshhooks)); @@ -594,9 +700,6 @@ setupvals(void) gettimeofday(&shtimer, &dummy_tz); /* init $SECONDS */ srand((unsigned int)(shtimer.tv_sec + shtimer.tv_usec)); /* seed $RANDOM */ - hostnam = (char *) zalloc(256); - gethostname(hostnam, 256); - /* Set default path */ path = (char **) zalloc(sizeof(*path) * 5); path[0] = ztrdup("/bin"); @@ -695,7 +798,8 @@ setupvals(void) * initialize `PWD' from `HOME' */ if (ispwd(home)) pwd = ztrdup(home); - else if ((ptr = zgetenv("PWD")) && ispwd(ptr)) + else if ((ptr = zgetenv("PWD")) && (strlen(ptr) < PATH_MAX) && + (ptr = metafy(ptr, -1, META_STATIC), ispwd(ptr))) pwd = ztrdup(ptr); else pwd = metafy(zgetcwd(), -1, META_DUP); @@ -706,12 +810,12 @@ setupvals(void) initlextabs(); /* initialize lexing tables */ createreswdtable(); /* create hash table for reserved words */ - createaliastable(); /* create hash table for aliases */ + createaliastables(); /* create hash tables for aliases */ createcmdnamtable(); /* create hash table for external commands */ createshfunctable(); /* create hash table for shell functions */ createbuiltintable(); /* create hash table for builtin commands */ createnameddirtable(); /* create hash table for named directories */ - createparamtable(); /* create paramater hash table */ + createparamtable(); /* create parameter hash table */ condtab = NULL; wrappers = NULL; @@ -754,7 +858,12 @@ setupvals(void) #endif } - times(&shtms); + get_usage(); + + /* Close the file descriptors we opened to block off 0 to 9 */ + for (i = 0; i < 10; i++) + if (close_fds[i]) + close(i); } /* Initialize signal handling */ @@ -763,6 +872,12 @@ setupvals(void) void init_signals(void) { + if (interact) { + int i; + signal_setmask(signal_mask(0)); + for (i=0; i= PATH_MAX) { - zerr("path too long: %s", s, 0); - return; + + { + /* Let source() complain if path is too long */ + VARARR(char, buf, strlen(h) + strlen(s) + 2); + sprintf(buf, "%s/%s", h, s); + unqueue_signals(); + source(buf); } - sprintf(buf, "%s/%s", h, s); - source(buf); } /**/ @@ -1009,7 +1110,7 @@ noop_function(void) /**/ mod_export void -noop_function_int(int nothing) +noop_function_int(UNUSED(int nothing)) { /* do nothing */ } @@ -1024,51 +1125,63 @@ noop_function_int(int nothing) /**/ mod_export ZleVoidFn trashzleptr = noop_function; /**/ -mod_export ZleVoidFn gotwordptr = noop_function; -/**/ mod_export ZleVoidFn refreshptr = noop_function; /**/ mod_export ZleVoidIntFn spaceinlineptr = noop_function_int; /**/ mod_export ZleReadFn zlereadptr = autoload_zleread; +/**/ +mod_export ZleVoidIntFn zlesetkeymapptr = noop_function_int; #else /* !LINKED_XMOD_zshQszle */ mod_export ZleVoidFn trashzleptr = noop_function; -mod_export ZleVoidFn gotwordptr = noop_function; mod_export ZleVoidFn refreshptr = noop_function; mod_export ZleVoidIntFn spaceinlineptr = noop_function_int; # ifdef UNLINKED_XMOD_zshQszle mod_export ZleReadFn zlereadptr = autoload_zleread; +mod_export ZleVoidIntFn zlesetkeymapptr = autoload_zlesetkeymap; # else /* !UNLINKED_XMOD_zshQszle */ mod_export ZleReadFn zlereadptr = fallback_zleread; +mod_export ZleVoidIntFn zlesetkeymapptr = noop_function_int; # endif /* !UNLINKED_XMOD_zshQszle */ #endif /* !LINKED_XMOD_zshQszle */ /**/ unsigned char * -autoload_zleread(char *lp, char *rp, int ha) +autoload_zleread(char **lp, char **rp, int ha, int con) { zlereadptr = fallback_zleread; if (load_module("zsh/zle")) load_module("zsh/compctl"); - return zleread(lp, rp, ha); + return zleread(lp, rp, ha, con); } /**/ mod_export unsigned char * -fallback_zleread(char *lp, char *rp, int ha) +fallback_zleread(char **lp, UNUSED(char **rp), UNUSED(int ha), UNUSED(int con)) { char *pptbuf; int pptlen; - pptbuf = unmetafy(promptexpand(lp, 0, NULL, NULL), &pptlen); + pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL), &pptlen); write(2, (WRITE_ARG_2_T)pptbuf, pptlen); free(pptbuf); + return (unsigned char *)shingetline(); } +/**/ +static void +autoload_zlesetkeymap(int mode) +{ + zlesetkeymapptr = noop_function_int; + load_module("zsh/zle"); + (*zlesetkeymapptr)(mode); +} + + /* compctl entry point pointers. Similar to the ZLE ones. */ /**/ @@ -1076,9 +1189,113 @@ mod_export CompctlReadFn compctlreadptr = fallback_compctlread; /**/ mod_export int -fallback_compctlread(char *name, char **args, char *ops, char *reply) +fallback_compctlread(char *name, UNUSED(char **args), UNUSED(Options ops), UNUSED(char *reply)) { zwarnnam(name, "option valid only in functions called from completion", NULL, 0); return 1; } + +/* + * This is real main entry point. This has to be mod_export'ed + * so zsh.exe can found it on Cygwin + */ + +/**/ +mod_export int +zsh_main(UNUSED(int argc), char **argv) +{ + char **t; + int t0; +#ifdef USE_LOCALE + setlocale(LC_ALL, ""); +#endif + + init_jobs(argv, environ); + + /* + * Provisionally set up the type table to allow metafication. + * This will be done properly when we have decided if we are + * interactive + */ + typtab['\0'] |= IMETA; + typtab[STOUC(Meta) ] |= IMETA; + typtab[STOUC(Marker)] |= IMETA; + for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++) + typtab[t0] |= ITOK | IMETA; + + for (t = argv; *t; *t = metafy(*t, -1, META_ALLOC), t++); + + zsh_name = argv[0]; + do { + char *arg0 = zsh_name; + if (!(zsh_name = strrchr(arg0, '/'))) + zsh_name = arg0; + else + zsh_name++; + if (*zsh_name == '-') + zsh_name++; + if (strcmp(zsh_name, "su") == 0) { + char *sh = zgetenv("SHELL"); + if (sh && *sh && arg0 != sh) + zsh_name = sh; + else + break; + } else + break; + } while (zsh_name); + + fdtable_size = zopenmax(); + fdtable = zshcalloc(fdtable_size); + + createoptiontable(); + emulate(zsh_name, 1); /* initialises most options */ + opts[LOGINSHELL] = (**argv == '-'); + opts[MONITOR] = 1; /* may be unset in init_io() */ + opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid()); + opts[USEZLE] = 1; /* may be unset in init_io() */ + parseargs(argv); /* sets INTERACTIVE, SHINSTDIN and SINGLECOMMAND */ + + SHTTY = -1; + init_io(); + setupvals(); + init_signals(); + init_bltinmods(); + run_init_scripts(); + init_misc(); + + for (;;) { + /* + * See if we can free up some of jobtab. + * We only do this at top level, because if we are + * executing stuff we may refer to them by job pointer. + */ + maybeshrinkjobtab(); + + do + loop(1,0); + while (tok != ENDINPUT && (tok != LEXERR || isset(SHINSTDIN))); + if (tok == LEXERR) { + /* Make sure a parse error exits with non-zero status */ + if (!lastval) + lastval = 1; + stopmsg = 1; + zexit(lastval, 0); + } + if (!(isset(IGNOREEOF) && interact)) { +#if 0 + if (interact) + fputs(islogin ? "logout\n" : "exit\n", shout); +#endif + zexit(lastval, 0); + continue; + } + noexitct++; + if (noexitct >= 10) { + stopmsg = 1; + zexit(lastval, 0); + } + zerrnam("zsh", (!islogin) ? "use 'exit' to exit." + : "use 'logout' to logout.", NULL, 0); + } +} diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst new file mode 100644 index 000000000..81a963b05 --- /dev/null +++ b/Test/A01grammar.ztst @@ -0,0 +1,450 @@ +# +# This file contains tests corresponding to the `Shell Grammar' texinfo node. +# + +%prep + + mkdir basic.tmp && cd basic.tmp + + touch foo bar + echo "'" >unmatched_quote.txt + +%test +# +# Tests for `Simple Commands and Pipelines' +# + echo foo | cat | sed 's/foo/bar/' +0:Basic pipeline handling +>bar + + false | true +0:Exit status of pipeline with builtins (true) + + true | false +1:Exit status of pipeline with builtins (false) + + fn() { local foo; read foo; print $foo; } + coproc fn + print -p coproc test output + read -p bar + print $bar +0:Basic coprocess handling +>coproc test output + + true | false && print true || print false +0:Basic sublist (i) +>false + + false | true && print true || print false +0:Basic sublist (ii) +>true + + (cd /NonExistentDirectory >&/dev/null) || print false +0:Basic subshell list with error +>false + + { cd /NonExistentDirectory >&/dev/null } || print false +0:Basic current shell list with error +>false + +# +# Tests for `Precommand Modifiers' +# + - $ZTST_testdir/../Src/zsh -fc "[[ \$0 = \"-$ZTST_testdir/../Src/zsh\" ]]" +0:`-' precommand modifier + + echo f* + noglob echo f* +0:`noglob' precommand modifier +>foo +>f* + + (exec /bin/sh; echo bar) +0:`exec' precommand modifier + + cat() { echo Function cat executed; } + command cat && unfunction cat +0:`command' precommand modifier +External command cat executed + + cd() { echo Not cd at all; } + builtin cd . && unfunction cd +0:`builtin' precommand modifier + +# +# Tests for `Complex Commands' +# + + if true; then + print true-1 + elif true; then + print true-2 + else + print false + fi +0:`if ...' (i) +>true-1 + + if false; then + print true-1 + elif true; then + print true-2 + else + print false + fi +0:`if ...' (ii) +>true-2 + + if false; then + print true-1 + elif false; then + print true-2 + else + print false + fi +0:`if ...' (iii) +>false + + if true; + : + fi +1d:`if ...' (iv) +?(eval):3: parse error near `fi' + + for name in word to term; do + print $name + done +0:`for' loop +>word +>to +>term + + for name + in word to term; do + print $name + done +0:`for' loop with newline before in keyword +>word +>to +>term + + for (( name = 0; name < 3; name++ )); do + print $name + done +0:arithmetic `for' loop +>0 +>1 +>2 + + for keyvar valvar in key1 val1 key2 val2; do + print key=$keyvar val=$valvar + done +0:enhanced `for' syntax with two loop variables +>key=key1 val=val1 +>key=key2 val=val2 + + for keyvar valvar stuffvar in keyA valA stuffA keyB valB stuffB; do + print key=$keyvar val=$valvar stuff=$stuffvar + done +0:enhanced `for' syntax with three loop variables +>key=keyA val=valA stuff=stuffA +>key=keyB val=valB stuff=stuffB + + for in in in in in stop; do + print in=$in + done +0:compatibility of enhanced `for' syntax with standard syntax +>in=in +>in=in +>in=in +>in=stop + + name=0 + while (( name < 3 )); do + print $name + (( name++ )) + done +0:`while' loop +>0 +>1 +>2 + + name=0 + until (( name == 3 )); do + print $name + (( name++ )) + done +0:`until' loop +>0 +>1 +>2 + + repeat 3 do + echo over and over + done +0:`repeat' loop +>over and over +>over and over +>over and over + + word=Trinity + case $word in + Michaelmas) print 0 + ;; + Hilary) print 1 + ;; + Trinity) print 2 + ;; + *) print 3 + ;; + esac +0:`case', old syntax +>2 + + word=Trinity + case $word in + (Michaelmas) print 0 + ;; + (Hilary) print 1 + ;; + (Trinity) print 2 + ;; + (*) print 3 + ;; + esac +0:`case', new syntax +>2 + + word=Hilary + case $word in + (Michaelmas) print 0 + ;; + (Hilary) print 1 + ;& + (Trinity) print 2 + ;& + (*) print 3 + ;; + esac +0:`case', new syntax, cascaded +>1 +>2 +>3 + +## Select now reads from stdin if the shell is not interactive. +## Its own output goes to stderr. + (COLUMNS=80 + PS3="input> " + select name in one two three; do + print $name + done) +0:`select' loop +<2 +?1) one 2) two 3) three +?input> input> +>two + + function name1 name2 () { print This is $0; } + name2 + name1 name2() { print This is still $0; } + name2 +0:`function' keyword +>This is name2 +>This is still name2 + + (time cat) >&/dev/null +0:`time' keyword (status only) + + if [[ -f foo && -d . && -n $ZTST_testdir ]]; then + true + else + false + fi +0:basic [[ ... ]] test + +# +# Current shell execution with try/always form. +# We put those with errors in subshells so that any unhandled error doesn't +# propagate. +# + + { + print The try block. + } always { + print The always block. + } + print After the always block. +0:Basic `always' syntax +>The try block. +>The always block. +>After the always block. + + ({ + print Position one. + print ${*this is an error*} + print Position two. + } always { + if (( TRY_BLOCK_ERROR )); then + print An error occurred. + else + print No error occurred. + fi + } + print Position three) +1:Always block with error not reset +>Position one. +>An error occurred. +?(eval):3: bad substitution + + ({ + print Stelle eins. + print ${*voici une erreur} + print Posizione due. + } always { + if (( TRY_BLOCK_ERROR )); then + print Erratum factum est. Retro ponetur. + (( TRY_BLOCK_ERROR = 0 )) + else + print unray touay foay anguageslay + fi + } + print Status after always block is $?.) +0:Always block with error reset +>Stelle eins. +>Erratum factum est. Retro ponetur. +>Status after always block is 1. +?(eval):3: bad substitution + +# Outputting of structures from the wordcode is distinctly non-trivial, +# we probably ought to have more like the following... + fn1() { { echo foo; } } + fn2() { { echo foo; } always { echo bar; } } + fn3() { ( echo foo; ) } + functions fn1 fn2 fn3 +0:Output of syntactic structures with and without always blocks +>fn1 () { +> { +> echo foo +> } +>} +>fn2 () { +> { +> echo foo +> } always { +> echo bar +> } +>} +>fn3 () { +> ( +> echo foo +> ) +>} + + +# +# Tests for `Alternate Forms For Complex Commands' +# + + if (true) { print true-1 } elif (true) { print true-2 } else { print false } + if (false) { print true-1 } elif (true) { print true-2 } else { print false } + if (false) { print true-1 } elif (false) { print true-2 } else { print false } +0:Alternate `if' with braces +>true-1 +>true-2 +>false + + if true; print true +0:Short form of `if' +>true + + for name ( word1 word2 word3 ) print $name +0:Form of `for' with parentheses. +>word1 +>word2 +>word3 + + for name in alpha beta gamma; print $name +0:Short form of `for' +>alpha +>beta +>gamma + + for (( val = 2; val < 10; val *= val )) print $val +0:Short arithmetic `for' +>2 +>4 + + foreach name ( verbiage words periphrasis ) + print $name + end +0:Csh-like `for' +>verbiage +>words +>periphrasis + +# see comment with braces used in if loops + val=0; + while (( val < 2 )) { print $((val++)); } +0:Alternative `while' +>0 +>1 + + val=2; + until (( val == 0 )) { print $((val--)); } +0:Alternative `until' +>2 +>1 + + repeat 3 print Hip hip hooray +0:Short `repeat' +>Hip hip hooray +>Hip hip hooray +>Hip hip hooray + + case bravo { + (alpha) print schmalpha + ;; + (bravo) print schmavo + ;; + (charlie) print schmarlie + ;; + } +0:`case' with braces +>schmavo + + print 'This test hangs the shell when it fails...' >&8 + name=0 +# The number 4375 here is chosen to produce more than 16384 bytes of output + while (( name < 4375 )); do + print -n $name + (( name++ )) + done < /dev/null | { read name; print done } +0:Bug regression: `while' loop with redirection and pipeline +>done + +# This used to be buggy and print X at the end of each iteration. + for f in 1 2 3 4; do + print $f || break + done && print X +0:Handling of ||'s and &&'s with a for loop in between +>1 +>2 +>3 +>4 +>X + +# Same bug for &&, used to print `no' at the end of each iteration + for f in 1 2 3 4; do + false && print strange + done || print no +0:Handling of &&'s and ||'s with a for loop in between +>no + + $ZTST_testdir/../Src/zsh -f unmatched_quote.txt +1:Parse error with file causes non-zero exit status +?unmatched_quote.txt:2: unmatched ' + + $ZTST_testdir/../Src/zsh -f