about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBart Schaefer <barts@users.sourceforge.net>2001-06-25 16:10:59 +0000
committerBart Schaefer <barts@users.sourceforge.net>2001-06-25 16:10:59 +0000
commit6e01a977cc49a19fe6b223b44e12ded7d9ef2061 (patch)
treeb7a434c6a8debd1b3a21306f83a332170d6e5dfd
parent19cfee56c99ba1bbf299d2d31f06d513fb9262ed (diff)
downloadzsh-6e01a977cc49a19fe6b223b44e12ded7d9ef2061.tar.gz
zsh-6e01a977cc49a19fe6b223b44e12ded7d9ef2061.tar.xz
zsh-6e01a977cc49a19fe6b223b44e12ded7d9ef2061.zip
Merge a few more dev changes before 4.0.2.
-rw-r--r--ChangeLog12
-rw-r--r--Completion/Base/Widget/_complete_debug24
-rw-r--r--Completion/Base/Widget/_complete_help92
-rw-r--r--Doc/Zsh/options.yo14
-rw-r--r--Src/options.c54
-rw-r--r--Src/subst.c183
-rw-r--r--Src/zsh.h135
-rw-r--r--Test/E01options.ztst616
8 files changed, 1033 insertions, 97 deletions
diff --git a/ChangeLog b/ChangeLog
index 924a90194..51ffdec59 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,7 +5,7 @@
 
 001-06-22  Andrej Borsenkow  <bor@zsh.org>
 
-        * 15021: Src/prompt.c, Doc/Zsh/prompt.yo: revert to old %l
+	* 15021: Src/prompt.c, Doc/Zsh/prompt.yo: revert to old %l
 	semantic; add new %y character instead as suggested by Wayne
 
 2001-06-21  Bart Schaefer  <schaefer@zsh.org>
@@ -17,6 +17,10 @@
 
 	* 15018: Completion/compinit: Fix exit status of compdef.
 
+	* 15002: Completion/Base/Widget/_complete_debug,
+	Completion/Base/Widget/_complete_help: Use `trap' to restore state
+	properly in case of keyboard interrupt.
+
 2001-06-20  Bart Schaefer  <schaefer@zsh.org>
 
 	* 14880: Completion/Base/Core/_main_complete: Fix insert-tab style
@@ -100,6 +104,12 @@
 	* 14883 (Jos Backus): Completion/Unix/Command/_mount: add code for
 	BSDish systems
 
+2001-06-12  Peter Stephenson  <pws@csr.com>
+
+	* 14858: Doc/Zsh/options.yo, Src/options.c, Src/subst.c,
+	Src/zsh.h, Test/E01options.ztst: KSH_TYPESET option allows
+	assignments after typeset not to be split.
+
 2001-06-12  Bart Schaefer  <schaefer@zsh.org>
 
 	* 14849: README: Request "reporter" output only if a bug cannot
diff --git a/Completion/Base/Widget/_complete_debug b/Completion/Base/Widget/_complete_debug
new file mode 100644
index 000000000..1f12e8b9f
--- /dev/null
+++ b/Completion/Base/Widget/_complete_debug
@@ -0,0 +1,24 @@
+#compdef -k complete-word \C-x?
+
+eval "$_comp_setup"
+
+(( $+_debug_count )) || integer -g _debug_count
+local tmp=${TMPPREFIX}${$}${words[1]:t}$[++_debug_count]
+local w="${(qq)words}"
+
+exec 3>&-	# Too bad if somebody else is using it ...
+[[ -t 2 ]] && { exec 3>&2 2>| $tmp ; trap 'exec 2>&3 3>&-' EXIT INT }
+
+setopt xtrace
+_main_complete
+integer ret=$?
+unsetopt xtrace
+
+[[ -t 3 ]] && {
+    print -sR "${VISUAL:-${EDITOR:-${PAGER:-more}}} ${(q)tmp} ;: $w"
+    _message -r "Trace output left in $tmp (up-history to view)"
+    [[ $compstate[nmatches] -le 1 && $compstate[list] != *force* ]] &&
+        compstate[list]='list force messages'
+}
+
+return ret
diff --git a/Completion/Base/Widget/_complete_help b/Completion/Base/Widget/_complete_help
new file mode 100644
index 000000000..69930e105
--- /dev/null
+++ b/Completion/Base/Widget/_complete_help
@@ -0,0 +1,92 @@
+#compdef -k complete-word \C-xh
+
+_complete_help() {
+  eval "$_comp_setup"
+
+  local _sort_tags=_help_sort_tags text i j k tmp
+  typeset -A help_funcs help_tags help_sfuncs help_styles
+
+  compadd() { return 1 }
+  zstyle() {
+    local _f="${${(@)${(@)funcstack[2,(i)_(main_complete|complete|approximate|normal)]}:#_(wanted|requested|all_labels|next_label)}% *}"
+
+    [[ -z "$_f" ]] && _f="${${(@)funcstack[2,(i)_(main_complete|complete|approximate|normal)]}:#_(wanted|requested|all_labels|next_label)}"
+
+    if [[ "$help_sfuncs[$2]" != *${_f}* ||
+          "$help_styles[${2}${_f}]" != *${3}* ]]; then
+      [[ "$help_sfuncs[$2]" != *${_f}* ]] &&
+          help_sfuncs[$2]="${help_sfuncs[$2]}:${_f}"
+      local _t
+
+      case "$1" in
+      -s) _t='[string] ';;
+      -a) _t='[array]  ';;
+      -h) _t='[assoc]  ';;
+      *)  _t='[boolean]';;
+      esac
+      help_styles[${2}${_f}]="${help_styles[${2}${_f}]},${_t} ${3}:${_f}"
+    fi
+
+    # No need to call the completers more than once with different match specs.
+
+    if [[ "$3" = matcher-list ]]; then
+      set -A "$4" ''
+    else
+      builtin zstyle "$@"
+    fi
+  }
+  trap 'unfunction compadd zstyle' EXIT INT
+
+  _main_complete
+
+  unfunction compadd zstyle
+  trap - EXIT INT
+
+  for i in "${(@ok)help_funcs}"; do
+    text="${text}
+tags in context :completion:${i}:"
+    tmp=()
+    for j in "${(@s.:.)help_funcs[$i][2,-1]}"; do
+      tmp=( "$tmp[@]" "${(@s.,.)help_tags[${i}${j}][2,-1]}" )
+    done
+    zformat -a tmp '  (' "$tmp[@]"
+    tmp=( '
+    '${^tmp}')' )
+    text="${text}${tmp}"
+  done
+
+  if [[ ${NUMERIC:-1} -ne 1 ]]; then
+    text="$text
+"
+    for i in "${(@ok)help_sfuncs}"; do
+      text="${text}
+styles in context ${i}"
+      tmp=()
+      for j in "${(@s.:.)help_sfuncs[$i][2,-1]}"; do
+        tmp=( "$tmp[@]" "${(@s.,.)help_styles[${i}${j}][2,-1]}" )
+      done
+      zformat -a tmp '  (' "$tmp[@]"
+      tmp=( '
+    '${^tmp}')' )
+      text="${text}${tmp}"
+    done
+  fi
+  compstate[list]='list force'
+  compstate[insert]=''
+
+  compadd -UX "$text[2,-1]" -n ''
+}
+
+_help_sort_tags() {
+  local f="${${(@)${(@)funcstack[3,(i)_(main_complete|complete|approximate|normal)]}:#_(wanted|requested|all_labels|next_label)}% *}"
+
+  if [[ "$help_funcs[$curcontext]" != *${f}* ||
+        "$help_tags[${curcontext}${f}]" != *(${(j:|:)~argv})* ]]; then
+    [[ "$help_funcs[$curcontext]" != *${f}* ]] &&
+        help_funcs[$curcontext]="${help_funcs[$curcontext]}:${f}"
+    help_tags[${curcontext}${f}]="${help_tags[${curcontext}${f}]},${argv}:${f}"
+    comptry "$@"
+  fi
+}
+
+_complete_help "$@"
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 490946cd5..564e6701b 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -668,6 +668,16 @@ Alters the way options settings are printed: instead of separate lists of
 set and unset options, all options are shown, marked `on' if
 they are in the non-default state, `off' otherwise.
 )
+pindex(KSH_TYPESET)
+cindex(argument splitting, in typeset etc.)
+cindex(ksh, argument splitting in typeset)
+item(tt(KSH_TYPESET) <K>)(
+Alters the way arguments to the tt(typeset) family of commands, including
+tt(declare), tt(export), tt(float), tt(integer), tt(local) and
+tt(readonly), are processed.  Without this option, zsh will perform normal
+word splitting after command and parameter expansion in arguments of an
+assignment; with it, word splitting does not take place in those cases.
+)
 pindex(LIST_AMBIGUOUS)
 cindex(ambiguous completion)
 cindex(completion, ambiguous)
@@ -757,6 +767,10 @@ not otherwise treated specially; it is passed to the command as a single
 argument, and not used as an actual parameter assignment.  For example, in
 tt(echo foo=~/bar:~/rod), both occurrences of tt(~) would be replaced.
 Note that this happens anyway with tt(typeset) and similar statements.
+
+This option respects the setting of the tt(KSH_TYPESET) option.  In other
+words, if both options are in effect, arguments looking like
+assignments will not undergo wordsplitting.
 )
 pindex(MAIL_WARNING)
 cindex(mail, warning of reading)
diff --git a/Src/options.c b/Src/options.c
index 3e130a63e..2c63f194a 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -69,6 +69,7 @@ mod_export HashTable optiontab;
  * to avoid formatting problems.
  */
 static struct optname optns[] = {
+{NULL, "aliases",	      OPT_EMULATE|OPT_ALL,	 ALIASESOPT},
 {NULL, "allexport",	      OPT_EMULATE,		 ALLEXPORT},
 {NULL, "alwayslastprompt",    OPT_ALL,			 ALWAYSLASTPROMPT},
 {NULL, "alwaystoend",	      0,			 ALWAYSTOEND},
@@ -90,6 +91,7 @@ static struct optname optns[] = {
 {NULL, "bgnice",	      OPT_EMULATE|OPT_NONBOURNE, BGNICE},
 {NULL, "braceccl",	      OPT_EMULATE,		 BRACECCL},
 {NULL, "bsdecho",	      OPT_EMULATE|OPT_SH,	 BSDECHO},
+{NULL, "cbases",	      0,			 CBASES},
 {NULL, "cdablevars",	      OPT_EMULATE,		 CDABLEVARS},
 {NULL, "chasedots",	      OPT_EMULATE,		 CHASEDOTS},
 {NULL, "chaselinks",	      OPT_EMULATE,		 CHASELINKS},
@@ -112,6 +114,7 @@ static struct optname optns[] = {
 {NULL, "flowcontrol",	      OPT_ALL,			 FLOWCONTROL},
 {NULL, "functionargzero",     OPT_EMULATE|OPT_NONBOURNE, FUNCTIONARGZERO},
 {NULL, "glob",		      OPT_EMULATE|OPT_ALL,	 GLOBOPT},
+{NULL, "globalexport",        OPT_EMULATE|OPT_ZSH,	 GLOBALEXPORT},
 {NULL, "globalrcs",           OPT_ALL,			 GLOBALRCS},
 {NULL, "globassign",	      OPT_EMULATE|OPT_CSH,	 GLOBASSIGN},
 {NULL, "globcomplete",	      0,			 GLOBCOMPLETE},
@@ -142,6 +145,7 @@ static struct optname optns[] = {
 {NULL, "kshautoload",	      OPT_EMULATE|OPT_BOURNE,	 KSHAUTOLOAD},
 {NULL, "kshglob",             OPT_EMULATE|OPT_KSH,       KSHGLOB},
 {NULL, "kshoptionprint",      OPT_EMULATE|OPT_KSH,	 KSHOPTIONPRINT},
+{NULL, "kshtypeset",          OPT_EMULATE|OPT_KSH,	 KSHTYPESET},
 {NULL, "listambiguous",	      OPT_ALL,			 LISTAMBIGUOUS},
 {NULL, "listbeep",	      OPT_ALL,			 LISTBEEP},
 {NULL, "listpacked",	      0,			 LISTPACKED},
@@ -161,6 +165,7 @@ static struct optname optns[] = {
 {NULL, "notify",	      OPT_ZSH,			 NOTIFY},
 {NULL, "nullglob",	      OPT_EMULATE,		 NULLGLOB},
 {NULL, "numericglobsort",     OPT_EMULATE,		 NUMERICGLOBSORT},
+{NULL, "octalzeroes",         OPT_EMULATE|OPT_SH,	 OCTALZEROES},
 {NULL, "overstrike",	      0,			 OVERSTRIKE},
 {NULL, "pathdirs",	      OPT_EMULATE,		 PATHDIRS},
 {NULL, "posixbuiltins",	      OPT_EMULATE|OPT_BOURNE,	 POSIXBUILTINS},
@@ -209,6 +214,7 @@ static struct optname optns[] = {
 {NULL, "promptvars",	      OPT_ALIAS, /* bash */	 PROMPTSUBST},
 {NULL, "stdin",		      OPT_ALIAS, /* ksh */	 SHINSTDIN},
 {NULL, "trackall",	      OPT_ALIAS, /* ksh */	 HASHCMDS},
+{NULL, "dvorak",	      0,			 DVORAK},
 {NULL, NULL, 0, 0}
 };
 
@@ -642,6 +648,9 @@ dosetopt(int optno, int value, int force)
 	    for (s = rparams; *s; s++)
 		restrictparam(*s);
 	}
+    } else if(!force && optno == EXECOPT && !value && interact) {
+	/* cannot set noexec when interactive */
+	return -1;
     } else if(!force && (optno == INTERACTIVE || optno == SHINSTDIN ||
 	    optno == SINGLECOMMAND)) {
 	if (opts[optno] == value)
@@ -691,3 +700,48 @@ dashgetfn(Param pm)
     *val = '\0';
     return buf;
 }
+
+/* Print option list for --help */
+
+/**/
+void
+printoptionlist(void)
+{
+    short *lp;
+    char c;
+
+    printf("\nNamed options:\n");
+    scanhashtable(optiontab, 1, 0, OPT_ALIAS, printoptionlist_printoption, 0);
+    printf("\nOption aliases:\n");
+    scanhashtable(optiontab, 1, OPT_ALIAS, 0, printoptionlist_printoption, 0);
+    printf("\nOption letters:\n");
+    for(lp = optletters, c = FIRST_OPT; c <= LAST_OPT; lp++, c++) {
+	if(!*lp)
+	    continue;
+	printf("  -%c  ", c);
+	printoptionlist_printequiv(*lp);
+    }
+}
+
+/**/
+static void
+printoptionlist_printoption(HashNode hn, int ignored)
+{
+    Optname on = (Optname) hn;
+
+    if(on->flags & OPT_ALIAS) {
+	printf("  --%-19s  ", on->nam);
+	printoptionlist_printequiv(on->optno);
+    } else
+	printf("  --%s\n", on->nam);
+}
+
+/**/
+static void
+printoptionlist_printequiv(int optno)
+{
+    int isneg = optno < 0;
+
+    optno *= (isneg ? -1 : 1);
+    printf("  equivalent to --%s%s\n", isneg ? "no-" : "", optns[optno-1].nam);
+}
diff --git a/Src/subst.c b/Src/subst.c
index 408a9d406..b5480d75e 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -50,7 +50,9 @@ mod_export void
 prefork(LinkList list, int flags)
 {
     LinkNode node;
+    int asssub = (flags & PF_TYPESET) && isset(KSHTYPESET);
 
+    queue_signals();
     for (node = firstnode(list); node; incnode(node)) {
 	char *str, c;
 
@@ -61,14 +63,18 @@ prefork(LinkList list, int flags)
 		setdata(node, (void *) getproc(str));	/* <(...) or >(...) */
 	    else
 		setdata(node, (void *) getoutputfile(str));	/* =(...) */
-	    if (!getdata(node))
+	    if (!getdata(node)) {
+		unqueue_signals();
 		return;
+	    }
 	} else {
 	    if (isset(SHFILEEXPANSION))
 		filesub((char **)getaddrdata(node),
 			flags & (PF_TYPESET|PF_ASSIGN));
-	    if (!(node = stringsubst(list, node, flags & PF_SINGLE)))
+	    if (!(node = stringsubst(list, node, flags & PF_SINGLE, asssub))) {
+		unqueue_signals();
 		return;
+	    }
 	}
     }
     for (node = firstnode(list); node; incnode(node)) {
@@ -82,14 +88,17 @@ prefork(LinkList list, int flags)
 			flags & (PF_TYPESET|PF_ASSIGN));
 	} else if (!(flags & PF_SINGLE))
 	    uremnode(list, node);
-	if (errflag)
+	if (errflag) {
+	    unqueue_signals();
 	    return;
+	}
     }
+    unqueue_signals();
 }
 
 /**/
 static LinkNode
-stringsubst(LinkList list, LinkNode node, int ssub)
+stringsubst(LinkList list, LinkNode node, int ssub, int asssub)
 {
     int qt;
     char *str3 = (char *)getdata(node);
@@ -203,6 +212,12 @@ stringsubst(LinkList list, LinkNode node, int ssub)
 	    str3 = str2;
 	    setdata(node, str3);
 	    continue;
+	} else if (asssub && ((c == '=') || c == Equals) && str != str3) {
+	    /*
+	     * We are in a normal argument which looks like an assignment
+	     * and is to be treated like one, with no word splitting.
+	     */
+	    ssub = 1;
 	}
 	str++;
     }
@@ -307,7 +322,7 @@ multsub(char **s, char ***a, int *isarr, char *sep)
 mod_export void
 filesub(char **namptr, int assign)
 {
-    char *sub = NULL, *str, *ptr;
+    char *eql = NULL, *sub = NULL, *str, *ptr;
     int len;
 
     filesubstr(namptr, assign);
@@ -316,7 +331,7 @@ filesub(char **namptr, int assign)
 	return;
 
     if (assign & PF_TYPESET) {
-	if ((*namptr)[1] && (sub = strchr(*namptr + 1, Equals))) {
+	if ((*namptr)[1] && (eql = sub = strchr(*namptr + 1, Equals))) {
 	    str = sub + 1;
 	    if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) {
 		sub[1] = '\0';
@@ -330,7 +345,9 @@ filesub(char **namptr, int assign)
     while ((sub = strchr(ptr, ':'))) {
 	str = sub + 1;
 	len = sub - *namptr;
-	if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) {
+	if (sub > eql &&
+	    (sub[1] == Tilde || sub[1] == Equals) &&
+	    filesubstr(&str, assign)) {
 	    sub[1] = '\0';
 	    *namptr = dyncat(*namptr, str);
 	}
@@ -702,17 +719,21 @@ get_intarg(char **s)
 /* Parsing for the (e) flag. */
 
 static int
-subst_parse_str(char **sp, int single)
+subst_parse_str(char **sp, int single, int err)
 {
     char *s;
 
     *sp = s = dupstring(*sp);
 
-    if (!parsestr(s)) {
+    if (!(err ? parsestr(s) : parsestrnoerr(s))) {
 	if (!single) {
+            int qt = 0;
+
 	    for (; *s; s++)
-		if (*s == Qstring)
+		if (!qt && *s == Qstring)
 		    *s = String;
+                else if (*s == Dnull)
+                    qt = !qt;
 	}
 	return 0;
     }
@@ -750,6 +771,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     int casmod = 0;
     int quotemod = 0, quotetype = 0, quoteerr = 0;
     int visiblemod = 0;
+    int shsplit = 0;
     char *sep = NULL, *spsep = NULL;
     char *premul = NULL, *postmul = NULL, *preone = NULL, *postone = NULL;
     char *replstr = NULL;	/* replacement string for /orig/repl */
@@ -971,6 +993,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    presc++;
 		    break;
 
+		case 'z':
+		    shsplit = 1;
+		    break;
+
 		default:
 		  flagerr:
 		    zerr("error in flags", NULL, 0);
@@ -1000,7 +1026,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		spbreak = 0;
 		s++;
 	    } else
-		spbreak = 1;
+		spbreak = 2;
 	} else if ((c == '#' || c == Pound) &&
 		   (iident(cc = s[1])
 		    || cc == '*' || cc == Star || cc == '@'
@@ -1053,7 +1079,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    s++;
 	v = (Value) NULL;
     } else if (aspar) {
-	if ((v = getvalue(&vbuf, &s, 1))) {
+	if ((v = fetchvalue(&vbuf, &s, 1, (qt ? SCANPM_DQUOTED : 0)))) {
 	    val = idbeg = getstrvalue(v);
 	    subexp = 1;
 	} else
@@ -1065,7 +1091,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	if (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s),
 			     (wantt ? -1 :
 			      ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
-			     hkeys|hvals|(arrasg ? SCANPM_ASSIGNING : 0))) ||
+			     hkeys|hvals|
+			     (arrasg ? SCANPM_ASSIGNING : 0)|
+			     (qt ? SCANPM_DQUOTED : 0))) ||
 	    (v->pm && (v->pm->flags & PM_UNSET)))
 	    vunset = 1;
 
@@ -1104,6 +1132,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    val = dyncat(val, "-unique");
 		if (f & PM_HIDE)
 		    val = dyncat(val, "-hide");
+		if (f & PM_HIDE)
+		    val = dyncat(val, "-hideval");
 		if (f & PM_SPECIAL)
 		    val = dyncat(val, "-special");
 		vunset = 0;
@@ -1126,6 +1156,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		isarr = 0;
 	    }
 	    pm = createparam(nulstring, isarr ? PM_ARRAY : PM_SCALAR);
+	    DPUTS(!pm, "BUG: parameter not created");
 	    if (isarr)
 		pm->u.arr = aval;
 	    else
@@ -1133,8 +1164,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    v = (Value) hcalloc(sizeof *v);
 	    v->isarr = isarr;
 	    v->pm = pm;
-	    v->b = -1;
-	    if (getindex(&s, v) || s == os)
+	    v->end = -1;
+	    if (getindex(&s, v, qt) || s == os)
 		break;
 	}
 	if ((isarr = v->isarr)) {
@@ -1149,9 +1180,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    if (v->pm->flags & PM_ARRAY) {
 		int tmplen = arrlen(v->pm->gets.afn(v->pm));
 
-		if (v->a < 0)
-		    v->a += tmplen + v->inv;
-		if (!v->inv && (v->a >= tmplen || v->a < 0))
+		if (v->start < 0)
+		    v->start += tmplen + v->inv;
+		if (!v->inv && (v->start >= tmplen || v->start < 0))
 		    vunset = 1;
 	    }
 	    if (!vunset) {
@@ -1225,6 +1256,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	if (!inbrace)
 	    break;
     }
+    if (inbrace &&
+	(c = *s) != '-' && c != '+' && c != ':' && c != '%'  && c != '/' &&
+	c != '=' && c != Equals &&
+	c != '#' && c != Pound &&
+	c != '?' && c != Quest &&
+	c != '}' && c != Outbrace) {
+	zerr("bad substitution", NULL, 0);
+	return NULL;
+    }
     if (isarr) {
 	if (nojoin)
 	    isarr = -1;
@@ -1353,7 +1393,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	case '-':
 	    if (vunset) {
 		val = dupstring(s);
-		multsub(&val, NULL, &isarr, NULL);
+		/*
+		 * This is not good enough for sh emulation!  Sh would
+		 * split unquoted substrings, yet not split quoted ones
+		 * (except according to $@ rules); but this leaves the
+		 * unquoted substrings unsplit, and other code below
+		 * for spbreak splits even within the quoted substrings.
+		 */
+		multsub(&val, (aspar ? NULL : &aval), &isarr, NULL);
 		copied = 1;
 	    }
 	    break;
@@ -1381,8 +1428,6 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    if (spsep || spbreak) {
 			aval = sepsplit(val, spsep, 0, 1);
 			isarr = 2;
-			sep = spsep = NULL;
-			spbreak = 0;
 			l = arrlen(aval);
 			if (l && !*(aval[l-1]))
 			    l--;
@@ -1420,6 +1465,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		}
 		*idend = sav;
 		copied = 1;
+		if (isarr) {
+		  if (nojoin)
+		    isarr = -1;
+		  if (qt && !getlen && isarr > 0 && !spsep && spbreak < 2) {
+		    val = sepjoin(aval, sep, 1);
+		    isarr = 0;
+		  }
+		  sep = spsep = NULL;
+		  spbreak = 0;
+		}
 	    }
 	    break;
 	case '?':
@@ -1440,7 +1495,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	case '#':
 	case Pound:
 	case '/':
-	    if (qt) {
+            /* This once was executed only `if (qt) ...'. But with that
+             * patterns in a expansion resulting from a ${(e)...} aren't
+             * tokenized even though this function thinks they are (it thinks
+             * they are because subst_parse_string() turns Qstring tokens
+             * into String tokens and for unquoted parameter expansions the
+             * lexer normally does tokenize patterns inside parameter
+             * expansions). */
+            {
 		int one = noerrs, oef = errflag, haserr;
 
 		if (!quoteerr)
@@ -1458,9 +1520,18 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		}
 	    }
 	    {
+#if 0
+		/*
+		 * This allows # and % to be at the start of
+		 * a parameter in the substitution, which is
+		 * a bit nasty, and can be done (although
+		 * less efficiently) with anchors.
+		 */
+
 		char t = s[-1];
 
 		singsub(&s);
+
 		if (t == '/' && (flags & SUB_SUBSTR)) {
 		    if ((c = *s) == '#' || c == '%') {
 			flags &= ~SUB_SUBSTR;
@@ -1471,6 +1542,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 			s++;
 		    }
 		}
+#else
+		singsub(&s);
+#endif
 	    }
 
 	    if (!vunset && isarr) {
@@ -1635,18 +1709,24 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		aval = arrdup(aval), copied = 1;
 	    ap = aval;
 	    for (; *ap; ap++) {
+		char *tmps;
 		unmetafy(*ap, &len);
 		untokenize(*ap);
-		*ap = unmetafy(promptexpand(metafy(*ap, len, META_NOALLOC),
-					    0, NULL, NULL), &len);
+		tmps = unmetafy(promptexpand(metafy(*ap, len, META_NOALLOC),
+					     0, NULL, NULL), &len);
+		*ap = dupstring(tmps);
+		free(tmps);
 	    }
 	} else {
+	    char *tmps;
 	    if (!copied)
 		val = dupstring(val), copied = 1;
 	    unmetafy(val, &len);
 	    untokenize(val);
-	    val = unmetafy(promptexpand(metafy(val, len, META_NOALLOC),
+	    tmps = unmetafy(promptexpand(metafy(val, len, META_NOALLOC),
 					0, NULL, NULL), &len);
+	    val = dupstring(tmps);
+	    free(tmps);
 	}
 	opts[PROMPTSUBST] = ops;
 	opts[PROMPTBANG] = opb;
@@ -1748,6 +1828,34 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    val = nicedupstring(val);
 	}
     }
+    if (shsplit) {
+	LinkList list = NULL;
+
+	if (isarr) {
+	    char **ap;
+	    for (ap = aval; *ap; ap++)
+		list = bufferwords(list, *ap, NULL);
+	    isarr = 0;
+	} else
+	    list = bufferwords(NULL, val, NULL);
+
+	if (!firstnode(list))
+	    val = dupstring("");
+	else if (!nextnode(firstnode(list)))
+	    val = getdata(firstnode(list));
+	else {
+	    char **ap;
+	    LinkNode node;
+
+	    aval = ap = (char **) zhalloc((countlinknodes(list) + 1) *
+					  sizeof(char *));
+	    for (node = firstnode(list); node; incnode(node))
+		*ap++ = (char *) getdata(node);
+	    *ap = NULL;
+	    mult_isarr = isarr = 2;
+	}
+	copied = 1;
+    }
     if (isarr) {
 	char *x;
 	char *y;
@@ -1784,7 +1892,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 
 	    *--fstr = Marker;
 	    init_list1(tl, fstr);
-	    if (!eval && !stringsubst(&tl, firstnode(&tl), ssub))
+	    if (!eval && !stringsubst(&tl, firstnode(&tl), ssub, 0))
 		return NULL;
 	    *str = aptr;
 	    tn = firstnode(&tl);
@@ -1792,7 +1900,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		if (prenum || postnum)
 		    x = dopadding(x, prenum, postnum, preone, postone,
 				  premul, postmul);
-		if (eval && subst_parse_str(&x, (qt && !nojoin)))
+		if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
 		    return NULL;
 		xlen = strlen(x);
 		for (tn = firstnode(&tl);
@@ -1828,7 +1936,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    if (prenum || postnum)
 		x = dopadding(x, prenum, postnum, preone, postone,
 			      premul, postmul);
-	    if (eval && subst_parse_str(&x, (qt && !nojoin)))
+	    if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
 		return NULL;
 	    xlen = strlen(x);
 	    strcatsub(&y, ostr, aptr, x, xlen, NULL, globsubst, copied);
@@ -1843,7 +1951,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		if (prenum || postnum)
 		    x = dopadding(x, prenum, postnum, preone, postone,
 				  premul, postmul);
-		if (eval && subst_parse_str(&x, (qt && !nojoin)))
+		if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
 		    return NULL;
 		if (qt && !*x && isarr != 2)
 		    y = dupstring(nulstring);
@@ -1859,7 +1967,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    if (prenum || postnum)
 		x = dopadding(x, prenum, postnum, preone, postone,
 			      premul, postmul);
-	    if (eval && subst_parse_str(&x, (qt && !nojoin)))
+	    if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
 		return NULL;
 	    xlen = strlen(x);
 	    *str = strcatsub(&y, aptr, aptr, x, xlen, fstr, globsubst, copied);
@@ -1878,7 +1986,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	if (prenum || postnum)
 	    x = dopadding(x, prenum, postnum, preone, postone,
 			  premul, postmul);
-	if (eval && subst_parse_str(&x, (qt && !nojoin)))
+	if (eval && subst_parse_str(&x, (qt && !nojoin), quoteerr))
 	    return NULL;
 	xlen = strlen(x);
 	*str = strcatsub(&y, ostr, aptr, x, xlen, fstr, globsubst, copied);
@@ -1905,15 +2013,18 @@ static char *
 arithsubst(char *a, char **bptr, char *rest)
 {
     char *s = *bptr, *t;
-    char buf[DIGBUFSIZE], *b = buf;
+    char buf[BDIGBUFSIZE], *b = buf;
     mnumber v;
 
     singsub(&a);
     v = matheval(a);
-    if (v.type & MN_FLOAT)
+    if ((v.type & MN_FLOAT) && !outputradix)
 	b = convfloat(v.u.d, 0, 0, NULL);
-    else
-	convbase(buf, v.u.l, 0);
+    else {
+	if (v.type & MN_FLOAT)
+	    v.u.l = (zlong) v.u.d;
+	convbase(buf, v.u.l, outputradix);
+    }
     t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) + 
 				 strlen(rest) + 1);
     t--;
@@ -1975,7 +2086,7 @@ modify(char **str, char **ptr)
 		if (*ptr1) {
 		    zsfree(hsubl);
 		    hsubl = ztrdup(ptr1);
-		}
+ 		}
 		if (!hsubl) {
 		    zerr("no previous substitution", NULL, 0);
 		    return;
diff --git a/Src/zsh.h b/Src/zsh.h
index 41add5c35..3b6c0aad4 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -30,7 +30,6 @@
 #define trashzle()      trashzleptr()
 #define zleread(X,Y,H)  zlereadptr(X,Y,H)
 #define spaceinline(X)  spaceinlineptr(X)
-#define gotword()       gotwordptr()
 #define zrefresh()      refreshptr()
 
 #define compctlread(N,A,O,R) compctlreadptr(N,A,O,R)
@@ -234,31 +233,31 @@ enum {
  * below.                                                              */
 
 enum {
-    WRITE,		/* > */
-    WRITENOW,		/* >| */
-    APP,		/* >> */
-    APPNOW,		/* >>| */
-    ERRWRITE,		/* &>, >& */
-    ERRWRITENOW,	/* >&| */
-    ERRAPP,		/* >>& */
-    ERRAPPNOW,		/* >>&| */
-    READWRITE,		/* <> */
-    READ,		/* < */
-    HEREDOC,		/* << */
-    HEREDOCDASH,	/* <<- */
-    HERESTR,		/* <<< */
-    MERGEIN,		/* <&n */
-    MERGEOUT,		/* >&n */
-    CLOSE,		/* >&-, <&- */
-    INPIPE,		/* < <(...) */
-    OUTPIPE		/* > >(...) */
+    REDIR_WRITE,		/* > */
+    REDIR_WRITENOW,		/* >| */
+    REDIR_APP,		/* >> */
+    REDIR_APPNOW,		/* >>| */
+    REDIR_ERRWRITE,		/* &>, >& */
+    REDIR_ERRWRITENOW,	/* >&| */
+    REDIR_ERRAPP,		/* >>& */
+    REDIR_ERRAPPNOW,		/* >>&| */
+    REDIR_READWRITE,		/* <> */
+    REDIR_READ,		/* < */
+    REDIR_HEREDOC,		/* << */
+    REDIR_HEREDOCDASH,	/* <<- */
+    REDIR_HERESTR,		/* <<< */
+    REDIR_MERGEIN,		/* <&n */
+    REDIR_MERGEOUT,		/* >&n */
+    REDIR_CLOSE,		/* >&-, <&- */
+    REDIR_INPIPE,		/* < <(...) */
+    REDIR_OUTPIPE		/* > >(...) */
 };
 
-#define IS_WRITE_FILE(X)      ((X)>=WRITE && (X)<=READWRITE)
+#define IS_WRITE_FILE(X)      ((X)>=REDIR_WRITE && (X)<=REDIR_READWRITE)
 #define IS_APPEND_REDIR(X)    (IS_WRITE_FILE(X) && ((X) & 2))
 #define IS_CLOBBER_REDIR(X)   (IS_WRITE_FILE(X) && ((X) & 1))
-#define IS_ERROR_REDIR(X)     ((X)>=ERRWRITE && (X)<=ERRAPPNOW)
-#define IS_READFD(X)          (((X)>=READWRITE && (X)<=MERGEIN) || (X)==INPIPE)
+#define IS_ERROR_REDIR(X)     ((X)>=REDIR_ERRWRITE && (X)<=REDIR_ERRAPPNOW)
+#define IS_READFD(X)          (((X)>=REDIR_READWRITE && (X)<=REDIR_MERGEIN) || (X)==REDIR_INPIPE)
 #define IS_REDIROP(X)         ((X)>=OUTANG && (X)<=TRINANG)
 
 /* Flags for input stack */
@@ -468,8 +467,8 @@ struct value {
     int isarr;
     Param pm;		/* parameter node                      */
     int inv;		/* should we return the index ?        */
-    int a;		/* first element of array slice, or -1 */
-    int b;		/* last element of array slice, or -1  */
+    int start;		/* first element of array slice, or -1 */
+    int end;		/* 1-rel last element of array slice, or -1 */
     char **arr;		/* cache for hash turned into array */
 };
 
@@ -487,7 +486,8 @@ typedef struct eprog *Eprog;
 
 struct funcdump {
     FuncDump next;		/* next in list */
-    char *name;			/* path name */
+    dev_t dev;			/* device */
+    ino_t ino;			/* indoe number */
     int fd;			/* file descriptor */
     Wordcode map;		/* pointer to header */
     Wordcode addr;		/* mapped region */
@@ -496,7 +496,7 @@ struct funcdump {
 };
 
 struct eprog {
-    int alloc;			/* EA_* below */
+    int flags;			/* EF_* below */
     int len;			/* total block length */
     int npats;			/* Patprog cache size */
     Patprog *pats;		/* the memory block, the patterns */
@@ -506,9 +506,10 @@ struct eprog {
     FuncDump dump;		/* dump file this is in */
 };
 
-#define EA_REAL 0
-#define EA_HEAP 1
-#define EA_MAP  2
+#define EF_REAL 1
+#define EF_HEAP 2
+#define EF_MAP  4
+#define EF_RUN  8
 
 typedef struct estate *Estate;
 
@@ -521,9 +522,9 @@ struct estate {
 typedef struct eccstr *Eccstr;
 
 struct eccstr {
-    Eccstr next;
+    Eccstr left, right;
     char *str;
-    wordcode offs;
+    wordcode offs, aoffs;
     int nfunc;
 };
 
@@ -659,8 +660,9 @@ struct eccstr {
 /* Defintions for job table and job control */
 /********************************************/
 
-/* size of job table */
-#define MAXJOB 50
+#ifdef NEED_LINUX_TASKS_H
+#include <linux/tasks.h>
+#endif
 
 /* entry in the job table */
 
@@ -751,6 +753,8 @@ struct dirsav {
     ino_t ino;
 };
 
+#define MAX_PIPESTATS 256
+
 /*******************************/
 /* Definitions for Hash Tables */
 /*******************************/
@@ -953,6 +957,7 @@ struct module {
     union {
 	void *handle;
 	Linkedmod linked;
+	char *alias;
     } u;
     LinkList deps;
     int wrapper;
@@ -964,6 +969,7 @@ struct module {
 #define MOD_LINKED  (1<<3)
 #define MOD_INIT_S  (1<<4)
 #define MOD_INIT_B  (1<<5)
+#define MOD_ALIAS   (1<<6)
 
 typedef int (*Module_func) _((Module));
 
@@ -1019,6 +1025,8 @@ struct patprog {
 #define PAT_STATIC	0x0040	/* Don't copy pattern to heap as per default */
 #define PAT_SCAN	0x0080	/* Scanning, so don't try must-match test */
 #define PAT_ZDUP        0x0100  /* Copy pattern in real memory */
+#define PAT_NOTSTART	0x0200	/* Start of string is not real start */
+#define PAT_NOTEND	0x0400	/* End of string is not real end */
 
 /* Globbing flags: lower 8 bits gives approx count */
 #define GF_LCMATCHUC	0x0100
@@ -1111,20 +1119,22 @@ struct param {
 #define PM_UNALIASED	(1<<13)	/* do not expand aliases when autoloading   */
 
 #define PM_HIDE		(1<<14)	/* Special behaviour hidden by local        */
-#define PM_TIED 	(1<<15)	/* array tied to colon-path or v.v.         */
+#define PM_HIDEVAL	(1<<15)	/* Value not shown in `typeset' commands    */
+#define PM_TIED 	(1<<16)	/* array tied to colon-path or v.v.         */
 
 /* Remaining flags do not correspond directly to command line arguments */
-#define PM_LOCAL	(1<<16) /* this parameter will be made local        */
-#define PM_SPECIAL	(1<<17) /* special builtin parameter                */
-#define PM_DONTIMPORT	(1<<18)	/* do not import this variable              */
-#define PM_RESTRICTED	(1<<19) /* cannot be changed in restricted mode     */
-#define PM_UNSET	(1<<20)	/* has null value                           */
-#define PM_REMOVABLE	(1<<21)	/* special can be removed from paramtab     */
-#define PM_AUTOLOAD	(1<<22) /* autoloaded from module                   */
-#define PM_NORESTORE	(1<<23)	/* do not restore value of local special    */
+#define PM_LOCAL	(1<<17) /* this parameter will be made local        */
+#define PM_SPECIAL	(1<<18) /* special builtin parameter                */
+#define PM_DONTIMPORT	(1<<19)	/* do not import this variable              */
+#define PM_RESTRICTED	(1<<20) /* cannot be changed in restricted mode     */
+#define PM_UNSET	(1<<21)	/* has null value                           */
+#define PM_REMOVABLE	(1<<22)	/* special can be removed from paramtab     */
+#define PM_AUTOLOAD	(1<<23) /* autoloaded from module                   */
+#define PM_NORESTORE	(1<<24)	/* do not restore value of local special    */
+#define PM_HASHELEM     (1<<25) /* is a hash-element */
 
 /* The option string corresponds to the first of the variables above */
-#define TYPESET_OPTSTR "aiEFALRZlurtxUhT"
+#define TYPESET_OPTSTR "aiEFALRZlurtxUhHT"
 
 /* These typeset options take an optional numeric argument */
 #define TYPESET_OPTNUM "LRZiEF"
@@ -1138,6 +1148,7 @@ struct param {
 #define SCANPM_MATCHMANY  (1<<5)
 #define SCANPM_ASSIGNING  (1<<6)
 #define SCANPM_KEYMATCH   (1<<7)
+#define SCANPM_DQUOTED    (1<<8)
 #define SCANPM_ISVAR_AT   ((-1)<<15)	/* Only sign bit is significant */
 
 /*
@@ -1205,13 +1216,14 @@ struct nameddir {
 #define PRINT_TYPE		(1<<1)
 #define PRINT_LIST		(1<<2)
 #define PRINT_KV_PAIR		(1<<3)
+#define PRINT_INCLUDEVALUE	(1<<4)
 
 /* flags for printing for the whence builtin */
-#define PRINT_WHENCE_CSH	(1<<4)
-#define PRINT_WHENCE_VERBOSE	(1<<5)
-#define PRINT_WHENCE_SIMPLE	(1<<6)
-#define PRINT_WHENCE_FUNCDEF	(1<<7)
-#define PRINT_WHENCE_WORD	(1<<8)
+#define PRINT_WHENCE_CSH	(1<<5)
+#define PRINT_WHENCE_VERBOSE	(1<<6)
+#define PRINT_WHENCE_SIMPLE	(1<<7)
+#define PRINT_WHENCE_FUNCDEF	(1<<9)
+#define PRINT_WHENCE_WORD	(1<<10)
 
 /***********************************/
 /* Definitions for history control */
@@ -1240,6 +1252,7 @@ struct histent {
 #define HIST_READ	0x00000004	/* Command was read back from disk*/
 #define HIST_DUP	0x00000008	/* Command duplicates a later line */
 #define HIST_FOREIGN	0x00000010	/* Command came from another shell */
+#define HIST_TMPSTORE	0x00000020	/* Kill when user enters another cmd */
 
 #define GETHIST_UPWARD  (-1)
 #define GETHIST_DOWNWARD  1
@@ -1294,6 +1307,7 @@ struct histent {
 
 enum {
     OPT_INVALID,
+    ALIASESOPT,
     ALLEXPORT,
     ALWAYSLASTPROMPT,
     ALWAYSTOEND,
@@ -1315,6 +1329,7 @@ enum {
     BGNICE,
     BRACECCL,
     BSDECHO,
+    CBASES,
     CDABLEVARS,
     CHASEDOTS,
     CHASELINKS,
@@ -1336,8 +1351,9 @@ enum {
     EXTENDEDHISTORY,
     FLOWCONTROL,
     FUNCTIONARGZERO,
-    GLOBALRCS,
     GLOBOPT,
+    GLOBALEXPORT,
+    GLOBALRCS,
     GLOBASSIGN,
     GLOBCOMPLETE,
     GLOBDOTS,
@@ -1367,6 +1383,7 @@ enum {
     KSHAUTOLOAD,
     KSHGLOB,
     KSHOPTIONPRINT,
+    KSHTYPESET,
     LISTAMBIGUOUS,
     LISTBEEP,
     LISTPACKED,
@@ -1386,6 +1403,7 @@ enum {
     NOTIFY,
     NULLGLOB,
     NUMERICGLOBSORT,
+    OCTALZEROES,
     OVERSTRIKE,
     PATHDIRS,
     POSIXBUILTINS,
@@ -1422,6 +1440,7 @@ enum {
     VERBOSE,
     XTRACE,
     USEZLE,
+    DVORAK,
     OPT_SIZE
 };
 
@@ -1508,7 +1527,11 @@ struct ttyinfo {
 #define TCSTANDOUTEND  22
 #define TCUNDERLINEEND 23
 #define TCHORIZPOS     24
-#define TC_COUNT       25
+#define TCUPCURSOR     25
+#define TCDOWNCURSOR   26
+#define TCLEFTCURSOR   27
+#define TCRIGHTCURSOR  28
+#define TC_COUNT       29
 
 #define tccan(X) (tclen[X])
 
@@ -1612,13 +1635,11 @@ struct heap {
 #endif
 ;
 
-# define LASTALLOC_RETURN return
-
 # define NEWHEAPS(h)    do { Heap _switch_oldheaps = h = new_heaps(); do
 # define OLDHEAPS       while (0); old_heaps(_switch_oldheaps); } while (0);
 
-# define SWITCHHEAPS(h)  do { Heap _switch_oldheaps = switch_heaps(h); do
-# define SWITCHBACKHEAPS while (0); switch_heaps(_switch_oldheaps); } while (0);
+# define SWITCHHEAPS(o, h)  do { o = switch_heaps(h); do
+# define SWITCHBACKHEAPS(o) while (0); switch_heaps(o); } while (0);
 
 /****************/
 /* Debug macros */
@@ -1666,12 +1687,6 @@ typedef void (*ZleVoidIntFn) _((int));
 typedef unsigned char * (*ZleReadFn) _((char *, char *, int));
 
 /***************************************/
-/* Pseudo-keyword to mark exportedness */
-/***************************************/
-
-#define mod_export
-
-/***************************************/
 /* Hooks in core.                      */
 /***************************************/
 
diff --git a/Test/E01options.ztst b/Test/E01options.ztst
new file mode 100644
index 000000000..8ffba78b7
--- /dev/null
+++ b/Test/E01options.ztst
@@ -0,0 +1,616 @@
+# Test various shell options.
+# Interactive options not tested here:
+#    ALWAYS_LAST_PROMPT
+#    ALWAYS_TO_END
+#    APPEND_HISTORY (history not maintained)
+#    AUTO_LIST
+#    AUTO_MENU
+#    AUTO_NAME_DIRS  (named directory table not maintained)
+#    AUTO_PARAM_KEYS
+#    AUTO_PARAM_SLASH
+#    AUTO_REMOVE_SLASH
+#    AUTO_RESUME
+#    BANG_HIST
+#    BASH_AUTO_LIST
+#    BEEP (!)
+#    BG_NICE
+#    CHECK_JOBS
+#    COMPLETE_ALIASES
+#    COMPLETE_IN_WORD
+#    CORRECT
+#    CORRECT_ALL
+#    CSH_JUNKIE_HISTORY
+#    DVORAK
+#    EXTENDED_HISTORY
+#    FLOW_CONTROL
+#    GLOB_COMPLETE
+#    HIST_ALLOW_CLOBBER
+#    HIST_BEEP
+#    HIST_EXPIRE_DUPS_FIRST
+#    HIST_FIND_NO_DUPS
+#    HIST_IGNORE_ALL_DUPS
+#    HIST_IGNORE_DUPS (-h)
+#    HIST_IGNORE_SPACE (-g)
+#    HIST_NO_FUNCTIONS
+#    HIST_NO_STORE
+#    HIST_REDUCE_BLANKS
+#    HIST_SAVE_NO_DUPS
+#    HIST_VERIFY
+#    HUP
+#    IGNORE_EOF
+#    INC_APPEND_HISTORY
+#    INTERACTIVE
+#    INTERACTIVE_COMMENTS
+#    LIST_AMBIGUOUS
+#    LIST_BEEP
+#    LIST_PACKED
+#    LIST_ROWS_FIRST
+#    LIST_TYPES
+#    LOGIN
+#    LONG_LIST_JOBS
+#    MAIL_WARNING
+#    MENU_COMPLETE
+#    MONITOR
+#    NOTIFY
+# The following require SHINSTDIN and are not (yet) tested:
+#    AUTO_CD
+#
+# Other difficult things I haven't done:
+#    GLOBAL_RCS (uses fixed files outside build area)
+#    HASH_CMDS     )
+#    HASH_DIRS     )  fairly seriously internal, hard to test at all
+#    HASH_LIST_ALL )
+
+%prep
+  mkdir options.tmp && cd options.tmp
+  mkdir tmpcd
+  touch tmpfile1 tmpfile2
+  mydir=$PWD
+  catpath=$(which cat)
+
+%test
+
+  alias echo='print foo'
+  unsetopt aliases
+  # use eval else aliases are all parsed at start
+  eval echo bar
+  setopt aliases
+  eval echo bar
+  unalias echo
+0:ALIASES option
+>bar
+>foo bar
+
+  setopt allexport
+  testpm1=exported
+  unsetopt allexport
+  testpm2=unexported
+  print ${(t)testpm1}
+  print ${(t)testpm2}
+0:ALL_EXPORT option
+>scalar-export
+>scalar
+  
+  # Count the number of directories on the stack.  Don't care what they are.
+  dircount() { dirs -v | tail -1 | awk '{ print $1 + 1}'; }
+  unsetopt autopushd
+  cd tmpcd
+  dircount
+  cd ..
+  setopt autopushd
+  cd tmpcd
+  dircount
+  cd ..
+  unsetopt autopushd
+0:AUTO_PUSHD option
+>1
+>2
+
+  unsetopt badpattern
+  print [a
+  setopt badpattern
+  print [b
+1:BAD_PATTERN option
+>[a
+?ZTST_execchunk:2: bad pattern: [b
+
+  unsetopt bareglobqual nomatch
+  print *(.)
+  setopt bareglobqual nomatch
+  print *(.)
+0:BARE_GLOB_QUAL option
+>*(.)
+>tmpfile1 tmpfile2
+
+  setopt braceccl
+  print {abcd}
+  unsetopt braceccl
+  print {abcd}
+0:BRACE_CCL option
+>a b c d
+>{abcd}
+
+  setopt bsdecho
+  echo "histon\nimpington"
+  echo -e "girton\ncottenham"
+  unsetopt bsdecho
+  echo "newnham\ncomberton"
+0:BSD_ECHO option
+>histon\nimpington
+>girton
+>cottenham
+>newnham
+>comberton
+
+  unsetopt c_bases
+  print $(( [#16]15 ))
+  print $(( [#8]9 ))
+  setopt c_bases
+  print $(( [#16]31 ))
+  print $(( [#8]17 ))
+  setopt octal_zeroes
+  print $(( [#8]19 ))
+  unsetopt c_bases octal_zeroes
+0:C_BASES option
+>16#F
+>8#11
+>0x1F
+>8#21
+>023
+
+  setopt cdablevars
+  # only absolute paths are eligible for ~-expansion
+  cdablevar1=tmpcd
+  (cd cdablevar1)
+  cdablevar2=$PWD/tmpcd
+  cd cdablevar2
+  cd ..
+  print back in ${PWD:t}
+  unsetopt cdablevars
+  cd cdablevar2
+1q:CDABLE_VARS option
+>`print -P '%~'`/tmpcd
+>back in options.tmp
+?ZTST_execchunk:cd:2: no such file or directory: cdablevar1
+?ZTST_execchunk:cd:2: no such file or directory: cdablevar2
+
+# CHASE_DOTS should go with CHASE_LINKS in B01cd.ztst
+# which saves me having to write it here.
+
+  setopt noclobber
+  rm -f foo1 bar1 rod1
+  echo waterbeach >foo1
+  (echo landbeach >foo1)
+  cat foo1
+  (echo lode >>bar1)
+  [[ -f bar1 ]] && print That shouldn\'t be there.
+  echo denny >rod1
+  echo wicken >>rod1
+  cat rod1
+  unsetopt noclobber
+  rm -f foo2 bar2 rod2
+  echo ely >foo2
+  echo march >foo2
+  cat foo2
+  echo wimpole >>bar2
+  cat bar2
+  echo royston >rod2
+  echo foxton >>rod2
+  cat rod2
+  rm -f foo* bar* rod*
+0:CLOBBER option
+>waterbeach
+>denny
+>wicken
+>march
+>wimpole
+>royston
+>foxton
+?ZTST_execchunk:2: file exists: foo1
+?ZTST_execchunk:2: no such file or directory: bar1
+
+   setopt cshjunkieloops
+   eval 'for f in swaffham bulbeck; print $f; end'
+   print next one should fail >&2
+   unsetopt cshjunkieloops
+   eval 'for f in chesterton arbury; print $f; end'
+1:CSH_JUNKIE_LOOPS option (for loop)
+>swaffham
+>bulbeck
+?next one should fail
+?ZTST_execchunk:-1: parse error near `end'
+
+  setopt cshjunkiequotes
+  print this should cause an error >&2
+  eval "print 'line one
+  line two'"
+  print this should not >&2
+  eval "print 'line three\\
+  line four'"
+  unsetopt cshjunkiequotes
+0:CSH_JUNKIE_QUOTES option
+>line three
+>  line four
+?this should cause an error
+?ZTST_execchunk:-1: unmatched '
+?this should not
+
+  nullcmd() { print '$NULLCMD run'; }
+  readnullcmd() { print 'Running $READNULLCMD'; cat; }
+  NULLCMD=nullcmd
+  READNULLCMD=readnullcmd
+  setopt cshnullcmd
+  rm -f foo
+  print "This should fail" >&2
+  (>foo)
+  print "This should succeed" >&2
+  print "These are the contents of foo" >foo
+  cat foo
+  print "This should also fail" >&2
+  (<foo)
+  unsetopt cshnullcmd
+  rm -f foo
+  >foo
+  <foo
+  rm -f foo
+0:CSH_NULL_CMD option
+>These are the contents of foo
+>Running $READNULLCMD
+>$NULLCMD run
+?This should fail
+?ZTST_execchunk:2: redirection with no command
+?This should succeed
+?This should also fail
+?ZTST_execchunk:2: redirection with no command
+
+# nomatch should be overridden by cshnullglob
+  setopt nomatch cshnullglob
+  print tmp* nothing* blah
+  print -n 'hoping for no match: ' >&2
+  (print nothing* blah)
+  print >&2
+  unsetopt cshnullglob nomatch
+  print tmp* nothing* blah
+  print nothing* blah
+0:CSH_NULL_GLOB option
+>tmpcd tmpfile1 tmpfile2 blah
+>tmpcd tmpfile1 tmpfile2 nothing* blah
+>nothing* blah
+?hoping for no match: ZTST_execchunk:2: no match
+?
+
+# The trick is to avoid =cat being expanded in the output while $catpath is.
+  setopt NO_equals
+  print -n trick; print =cat
+  setopt equals
+  print -n trick; print =cat
+0q:EQUALS option
+>trick=cat
+>trick$catpath
+
+# explanation of expected TRAPZERR output:  from false and from
+# testfn() with ERR_EXIT on (hmm, should we really get a second one from
+# the function exiting?), then from the false only with ERR_EXIT off.
+  TRAPZERR() { print ZERR trapped; }
+  testfn() { setopt localoptions $2; print $1 before; false; print $1 after; }
+  (testfn on errexit)
+  testfn off
+  unfunction TRAPZERR testfn
+0:ERR_EXIT option
+>on before
+>ZERR trapped
+>ZERR trapped
+>off before
+>ZERR trapped
+>off after
+
+  (print before; setopt noexec; print after)
+0:NO_EXEC option
+>before
+
+  # The EXTENDED_GLOB test doesn't test globbing fully --- it just tests
+  # that certain patterns are treated literally with the option off
+  # and as patterns with the option on.
+  testfn() { print -n "$1 $2 $3 "; if [[ $1 = ${~2} ]];
+             then print yes; else print no; fi; }
+  tests=('a#' '?~b' '^aa')
+  strings=('a' 'aa' 'b' 'a#' '?~b' '^aa')
+  for opt in noextendedglob extendedglob; do
+    setopt $opt
+    for test in $tests; do
+      for string in $strings; do
+         testfn $string $test $opt
+      done
+    done
+  done
+0:EXTENDED_GLOB option
+>a a# noextendedglob no
+>aa a# noextendedglob no
+>b a# noextendedglob no
+>a# a# noextendedglob yes
+>?~b a# noextendedglob no
+>^aa a# noextendedglob no
+>a ?~b noextendedglob no
+>aa ?~b noextendedglob no
+>b ?~b noextendedglob no
+>a# ?~b noextendedglob no
+>?~b ?~b noextendedglob yes
+>^aa ?~b noextendedglob no
+>a ^aa noextendedglob no
+>aa ^aa noextendedglob no
+>b ^aa noextendedglob no
+>a# ^aa noextendedglob no
+>?~b ^aa noextendedglob no
+>^aa ^aa noextendedglob yes
+>a a# extendedglob yes
+>aa a# extendedglob yes
+>b a# extendedglob no
+>a# a# extendedglob no
+>?~b a# extendedglob no
+>^aa a# extendedglob no
+>a ?~b extendedglob yes
+>aa ?~b extendedglob no
+>b ?~b extendedglob no
+>a# ?~b extendedglob no
+>?~b ?~b extendedglob no
+>^aa ?~b extendedglob no
+>a ^aa extendedglob yes
+>aa ^aa extendedglob no
+>b ^aa extendedglob yes
+>a# ^aa extendedglob yes
+>?~b ^aa extendedglob yes
+>^aa ^aa extendedglob yes
+
+  foo() { print My name is $0; }
+  unsetopt functionargzero
+  foo
+  setopt functionargzero
+  foo
+  unfunction foo
+0:FUNCTION_ARGZERO option
+>My name is ZTST_execchunk
+>My name is foo
+
+  setopt _NO_glob_
+  print tmp*
+  set -o glob
+  print tmp*
+0:GLOB option
+>tmp*
+>tmpcd tmpfile1 tmpfile2
+
+  showit() { local v;
+             for v in first second third; do
+               eval print \$$v \$\{\(t\)$v\}
+	     done;
+           }      
+  setit() { typeset -x first=inside1;
+            typeset +g -x second=inside2;
+            typeset -g -x third=inside3;
+            showit;
+          }
+  first=outside1 second=outside2 third=outside3
+  unsetopt globalexport
+  setit
+  showit
+  setopt globalexport
+  setit
+  showit
+  unfunction setit showit
+0:GLOBAL_EXPORT option
+>inside1 scalar-local-export
+>inside2 scalar-local-export
+>inside3 scalar-export
+>outside1 scalar
+>outside2 scalar
+>inside3 scalar-export
+>inside1 scalar-export
+>inside2 scalar-local-export
+>inside3 scalar-export
+>inside1 scalar-export
+>outside2 scalar
+>inside3 scalar-export
+
+  setopt globassign
+  foo=tmp*
+  print $foo
+  unsetopt globassign
+  foo=tmp*
+  print $foo
+0:GLOB_ASSIGN option
+>tmpcd tmpfile1 tmpfile2
+>tmp*
+
+  mkdir onlysomefiles
+  touch onlysomefiles/.thisfile onlysomefiles/thatfile
+  setopt globdots
+  print onlysomefiles/*
+  unsetopt globdots
+  print onlysomefiles/*
+  rm -rf onlysomefiles
+0:GLOB_DOTS option
+>onlysomefiles/.thisfile onlysomefiles/thatfile
+>onlysomefiles/thatfile
+
+  # we've tested this enough times already...
+  # could add some stuff for other sorts of expansion
+  foo='tmp*'
+  setopt globsubst
+  print ${foo}
+  unsetopt globsubst
+  print ${foo}
+0:GLOB_SUBST option
+>tmpcd tmpfile1 tmpfile2
+>tmp*
+
+  setopt ignorebraces
+  echo X{a,b}Y
+  unsetopt ignorebraces
+  echo X{a,b}Y
+0:IGNORE_BRACES option
+>X{a,b}Y
+>XaY XbY
+
+  setopt ksh_arrays
+  array=(one two three)
+  print $array $array[2]
+  print ${array[0]} ${array[1]} ${array[2]} ${array[3]}
+  unsetopt ksh_arrays
+  print $array $array[2]
+  print ${array[0]} ${array[1]} ${array[2]} ${array[3]}
+  unset array
+0:KSH_ARRAYS option
+>one one[2]
+>one two three
+>one two three two
+>one one two three
+
+  fpath=(.)
+  echo >foo 'echo foo loaded; foo() { echo foo run; }'
+  echo >bar 'bar() { echo bar run; }'
+  setopt kshautoload
+  autoload foo bar
+  foo
+  bar
+  unfunction foo bar
+  unsetopt kshautoload
+  autoload foo bar
+  foo
+  bar
+0:KSH_AUTOLOAD option
+>foo loaded
+>foo run
+>bar run
+>foo loaded
+>bar run
+
+# ksh_glob is tested by the glob tests.
+
+  setopt kshoptionprint globassign
+  print set
+  setopt | grep kshoptionprint
+  setopt | grep globassign
+  unsetopt kshoptionprint
+  print unset
+  setopt | grep kshoptionprint
+  setopt | grep globassign
+  unsetopt globassign
+0:KSH_OPTION_PRINT option
+>set
+>kshoptionprint        on
+>globassign            on
+>unset
+>globassign
+
+  setopt kshtypeset
+  ktvars=(ktv1 ktv2)
+  typeset ktfoo=`echo arg1 arg2` $ktvars
+  print $+ktv1 $+ktv2 $+ktv3
+  print $ktfoo
+  unsetopt kshtypeset
+  typeset noktfoo=`echo noktarg1 noktarg2`
+  print $noktfoo
+  print $+noktarg1 $+noktarg2
+  unset ktfoo ktv1 ktv2 noktfoo noktarg2
+0:KSH_TYPESET option
+>1 1 0
+>arg1 arg2
+>noktarg1
+>0 1
+
+  showopt() { setopt | egrep 'localoptions|ksharrays'; }
+  f1() { setopt localoptions ksharrays; showopt }
+  f2() { setopt ksharrays; showopt }
+  setopt kshoptionprint
+  showopt
+  f1
+  showopt
+  f2
+  showopt
+  unsetopt ksh_arrays
+0:LOCAL_OPTIONS option
+>ksharrays             off
+>localoptions          off
+>ksharrays             on
+>localoptions          on
+>ksharrays             off
+>localoptions          off
+>ksharrays             on
+>localoptions          off
+>ksharrays             on
+>localoptions          off
+
+# LOCAL_TRAPS was tested in C03traps (phew).
+
+  fn() {
+    local HOME=/any/old/name
+    print -l var=~ 'anything goes/here'=~ split=`echo maybe not`;
+  }
+  setopt magicequalsubst
+  fn
+  setopt kshtypeset
+  fn
+  unsetopt magicequalsubst kshtypeset
+  fn
+0:MAGIC_EQUAL_SUBST option
+>var=/any/old/name
+>anything goes/here=/any/old/name
+>split=maybe
+>not
+>var=/any/old/name
+>anything goes/here=/any/old/name
+>split=maybe not
+>var=~
+>anything goes/here=~
+>split=maybe
+>not
+
+  setopt MARK_DIRS
+  print tmp*
+  unsetopt MARK_DIRS
+  print tmp*
+0:MARK_DIRS option
+>tmpcd/ tmpfile1 tmpfile2
+>tmpcd tmpfile1 tmpfile2
+
+# maybe should be in A04redirect
+  print "This is in1" >in1
+  print "This is in2" >in2
+  unsetopt multios
+  print Test message >foo1 >foo2
+  print foo1: $(<foo1)
+  print foo2: $(<foo2)
+  cat <in1 <in2
+  setopt multios
+  print Test message >foo1 >foo2
+  sleep 1   # damn, race in multios
+  print foo1: $(<foo1)
+  print foo2: $(<foo2)
+  cat <in1 <in2
+  rm -f foo1 foo2 in1 in2
+0:MULTIOS option
+>foo1:
+>foo2: Test message
+>This is in2
+>foo1: Test message
+>foo2: Test message
+>This is in1
+>This is in2
+
+# tried this with other things, but not on its own, so much.
+  unsetopt nomatch
+  print with nonomatch: flooble*
+  setopt nomatch
+  print with nomatch flooble*
+1:NOMATCH option
+>with nonomatch: flooble*
+?ZTST_execchunk:2: no matches found: flooble*
+
+# NULL_GLOB should override NONOMATCH...
+  setopt nullglob nomatch
+  print frooble* tmp*
+  unsetopt nullglob nomatch
+  print frooble* tmp*
+0:NULL_GLOB option
+>tmpcd tmpfile1 tmpfile2
+>frooble* tmpcd tmpfile1 tmpfile2