about summary refs log tree commit diff
path: root/Src/builtin.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/builtin.c')
-rw-r--r--Src/builtin.c1512
1 files changed, 985 insertions, 527 deletions
diff --git a/Src/builtin.c b/Src/builtin.c
index 31f396d93..dd0d3e523 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -42,29 +42,30 @@ static struct builtin builtins[] =
     BUILTIN("[", 0, bin_test, 0, -1, BIN_BRACKET, NULL, NULL),
     BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
     BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL),
-    BUILTIN("alias", BINF_MAGICEQUALS, bin_alias, 0, -1, 0, "Lgmr", NULL),
-    BUILTIN("autoload", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "t", "u"),
+    BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmr", NULL),
+    BUILTIN("autoload", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "tUXwkz", "u"),
     BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL),
     BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL),
     BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
     BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "LRUZfilrtux", NULL),
+    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghilrtux", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
     BUILTIN("echo", BINF_PRINTOPTS | BINF_ECHOPTS, bin_print, 0, -1, BIN_ECHO, "neE", "-"),
     BUILTIN("echotc", 0, bin_echotc, 1, -1, 0, NULL, NULL),
-    BUILTIN("emulate", 0, bin_emulate, 1, 1, 0, "R", NULL),
+    BUILTIN("emulate", 0, bin_emulate, 1, 1, 0, "LR", NULL),
     BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL),
     BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
     BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
-    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRUZfilrtu", "x"),
+    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "EFLRTUZafhilrtu", "xg"),
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
     BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
-    BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtu", NULL),
+    BUILTIN("float", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "EFghlrtux", "E"),
+    BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtuU", NULL),
     BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
     BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
     BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "dfmrv", NULL),
@@ -74,11 +75,11 @@ static struct builtin builtins[] =
 #endif
 
     BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "nrdDfEim", "l"),
-    BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "lrtux", "i"),
+    BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ghlrtux", "i"),
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
-    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "LRUZilrtu", NULL),
+    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZahilrtu", NULL),
     BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
     BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
 
@@ -86,14 +87,18 @@ static struct builtin builtins[] =
     BUILTIN("mem", 0, bin_mem, 0, 0, 0, "v", NULL),
 #endif
 
+#if defined(ZSH_PAT_DEBUG)
+    BUILTIN("patdebug", 0, bin_patdebug, 1, -1, 0, "p", NULL),
+#endif
+
     BUILTIN("popd", 0, bin_cd, 0, 2, BIN_POPD, NULL, NULL),
-    BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPnrslzNu0123456789pioOcm-", NULL),
+    BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPbnrslzNu0123456789pioOcm-", NULL),
     BUILTIN("pushd", 0, bin_cd, 0, 2, BIN_PUSHD, NULL, NULL),
     BUILTIN("pushln", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"),
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
     BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL),
     BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL),
-    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "LRUZfiltux", "r"),
+    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghiltux", "r"),
     BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"),
     BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
     BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL),
@@ -107,7 +112,7 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL),
     BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
-    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "LRUZfilrtuxm", NULL),
+    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghilrtuxm", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
@@ -118,10 +123,8 @@ static struct builtin builtins[] =
     BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsw", NULL),
     BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsw", "ca"),
     BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
-
-#ifdef DYNAMIC
-    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "Laudi", NULL),
-#endif
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcfdipue", NULL),
+    BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
 };
 
 /****************************************/
@@ -131,7 +134,7 @@ static struct builtin builtins[] =
 /* hash table containing builtin commands */
 
 /**/
-HashTable builtintab;
+mod_export HashTable builtintab;
  
 /**/
 void
@@ -142,6 +145,7 @@ createbuiltintable(void)
     builtintab->hash        = hasher;
     builtintab->emptytable  = NULL;
     builtintab->filltable   = NULL;
+    builtintab->cmpnodes    = strcmp;
     builtintab->addnode     = addhashnode;
     builtintab->getnode     = gethashnode;
     builtintab->getnode2    = gethashnode2;
@@ -206,9 +210,10 @@ int
 execbuiltin(LinkList args, Builtin bn)
 {
     LinkNode n;
-    char ops[MAX_OPS], *arg, *pp, *name, **argv, **oargv, *optstr;
+    char ops[MAX_OPS], *arg, *pp, *name, *optstr;
     char *oxarg, *xarg = NULL;
-    int flags, sense, argc = 0, execop;
+    char typenumstr[] = TYPESET_OPTNUM;
+    int flags, sense, argc = 0, execop, xtr = isset(XTRACE), lxarg = 0;
 
     /* initialise some static variables */
     auxdata = NULL;
@@ -220,14 +225,11 @@ execbuiltin(LinkList args, Builtin bn)
 
     arg = (char *) ugetnode(args);
 
-#ifdef DYNAMIC
     if (!bn->handlerfunc) {
 	zwarnnam(name, "autoload failed", NULL, 0);
 	deletebuiltin(bn->nam);
 	return 1;
     }
-#endif
-
     /* get some information about the command */
     flags = bn->flags;
     optstr = bn->optstr;
@@ -249,12 +251,21 @@ execbuiltin(LinkList args, Builtin bn)
 		    break;
 	    }
 	    /* save the options in xarg, for execution tracing */
-	    if (xarg) {
-		oxarg = tricat(xarg, " ", arg);
-		zsfree(xarg);
-		xarg = oxarg;
-	    } else
-		xarg = ztrdup(arg);
+	    if (xtr) {
+		if (xarg) {
+		    int l = strlen(arg) + lxarg + 1;
+
+		    oxarg = zhalloc(l + 1);
+		    strcpy(oxarg, xarg);
+		    oxarg[lxarg] = ' ';
+		    strcpy(oxarg + lxarg + 1, arg);
+		    xarg = oxarg;
+		    lxarg = l + 1;
+		} else {
+		    xarg = dupstring(arg);
+		    lxarg = strlen(xarg);
+		}
+	    }
 	    /* handle -- or - (ops['-']), and + (ops['-'] and ops['+']) */
 	    if (arg[1] == '-')
 		arg++;
@@ -273,8 +284,7 @@ execbuiltin(LinkList args, Builtin bn)
 	    /* "typeset" may take a numeric argument *
 	     * at the tail of the options            */
 	    if (idigit(*arg) && (flags & BINF_TYPEOPT) &&
-		(arg[-1] == 'L' || arg[-1] == 'R' ||
-		 arg[-1] == 'Z' || arg[-1] == 'i'))
+		strchr(typenumstr, arg[-1]))
 		auxlen = (int)zstrtol(arg, &arg, 10);
 	    /* The above loop may have exited on an invalid option.  (We  *
 	     * assume that any option requiring metafication is invalid.) */
@@ -282,7 +292,6 @@ execbuiltin(LinkList args, Builtin bn)
 		if(*arg == Meta)
 		    *++arg ^= 32;
 		zerr("bad option: -%c", NULL, *arg);
-		zsfree(xarg);
 		return 1;
 	    }
 	    arg = (char *) ugetnode(args);
@@ -300,9 +309,9 @@ execbuiltin(LinkList args, Builtin bn)
 		auxdata = arg;
 		arg = (char *) ugetnode(args);
 	    }
-	    /* for "typeset", -L, -R, -Z and -i take a numeric extra argument */
-	    if ((flags & BINF_TYPEOPT) && (execop == 'L' || execop == 'R' ||
-		execop == 'Z' || execop == 'i') && arg && idigit(*arg)) {
+	    /* some "typeset" options take a numeric extra argument */
+	    if ((flags & BINF_TYPEOPT) && strchr(typenumstr, execop) &&
+		arg && idigit(*arg)) {
 		auxlen = atoi(arg);
 		arg = (char *) ugetnode(args);
 	    }
@@ -322,39 +331,42 @@ execbuiltin(LinkList args, Builtin bn)
 	while (n)
 	    argc++, incnode(n);
     }
-    /* Get the actual arguments, into argv.  Oargv saves the *
-     * beginning of the array for later reference.           */
-    oargv = argv = (char **)ncalloc(sizeof(char **) * (argc + 1));
-    if ((*argv++ = arg))
-	while ((*argv++ = (char *)ugetnode(args)));
-    argv = oargv;
-    if (errflag) {
-	zsfree(xarg);
-	errflag = 0;
-	return 1;
-    }
+    {
+	VARARR(char *, argarr, (argc + 1));
+	char **argv, **oargv;
+
+	/* Get the actual arguments, into argv.  Oargv saves the *
+	 * beginning of the array for later reference.           */
+	oargv = argv = argarr;
+	if ((*argv++ = arg))
+	    while ((*argv++ = (char *)ugetnode(args)));
+	argv = oargv;
+	if (errflag) {
+	    errflag = 0;
+	    return 1;
+	}
 
-    /* check that the argument count lies within the specified bounds */
-    if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) {
-	zwarnnam(name, (argc < bn->minargs)
-		? "not enough arguments" : "too many arguments", NULL, 0);
-	zsfree(xarg);
-	return 1;
-    }
+	/* check that the argument count lies within the specified bounds */
+	if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) {
+	    zwarnnam(name, (argc < bn->minargs)
+		     ? "not enough arguments" : "too many arguments", NULL, 0);
+	    return 1;
+	}
 
-    /* display execution trace information, if required */
-    if (isset(XTRACE)) {
-	fprintf(stderr, "%s%s", (prompt4) ? prompt4 : "", name);
-	if (xarg)
-	    fprintf(stderr, " %s", xarg);
-	while (*oargv)
-	    fprintf(stderr, " %s", *oargv++);
-	fputc('\n', stderr);
-	fflush(stderr);
+	/* display execution trace information, if required */
+	if (xtr) {
+	    printprompt4();
+	    fprintf(xtrerr, "%s", name);
+	    if (xarg)
+		fprintf(xtrerr, " %s", xarg);
+	    while (*oargv)
+		fprintf(xtrerr, " %s", *oargv++);
+	    fputc('\n', xtrerr);
+	    fflush(xtrerr);
+	}
+	/* call the handler function, and return its return value */
+	return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid);
     }
-    zsfree(xarg);
-    /* call the handler function, and return its return value */
-    return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid);
 }
 
 /* Enable/disable an element in one of the internal hash tables.  *
@@ -368,7 +380,7 @@ bin_enable(char *name, char **argv, char *ops, int func)
     HashTable ht;
     HashNode hn;
     ScanFunc scanfunc;
-    Comp com;
+    Patprog pprog;
     int flags1 = 0, flags2 = 0;
     int match = 0, returnval = 0;
 
@@ -405,8 +417,8 @@ bin_enable(char *name, char **argv, char *ops, int func)
 	for (; *argv; argv++) {
 	    /* parse pattern */
 	    tokenize(*argv);
-	    if ((com = parsereg(*argv)))
-		match += scanmatchtable(ht, com, 0, 0, scanfunc, 0);
+	    if ((pprog = patcompile(*argv, PAT_STATIC, 0)))
+		match += scanmatchtable(ht, pprog, 0, 0, scanfunc, 0);
 	    else {
 		untokenize(*argv);
 		zwarnnam(name, "bad pattern : %s", *argv, 0);
@@ -537,9 +549,7 @@ bin_set(char *nam, char **args, char *ops, int func)
     } else {
 	/* set shell arguments */
 	freearray(pparams);
-	PERMALLOC {
-	    pparams = arrdup(args);
-	} LASTALLOC;
+	pparams = zarrdup(args);
     }
     return 0;
 }
@@ -567,7 +577,7 @@ bin_pwd(char *name, char **argv, char *ops, int func)
 /* the directory stack */
  
 /**/
-LinkList dirstack;
+mod_export LinkList dirstack;
  
 /* dirs: list the directory stack, or replace it with a provided list */
 
@@ -598,15 +608,13 @@ bin_dirs(char *name, char **argv, char *ops, int func)
 	return 0;
     }
     /* replace the stack with the specified directories */
-    PERMALLOC {
-	l = newlinklist();
-	if (*argv) {
-	    while (*argv)
-		addlinknode(l, ztrdup(*argv++));
-	    freelinklist(dirstack, freestr);
-	    dirstack = l;
-	}
-    } LASTALLOC;
+    l = znewlinklist();
+    if (*argv) {
+	while (*argv)
+	    zaddlinknode(l, ztrdup(*argv++));
+	freelinklist(dirstack, freestr);
+	dirstack = l;
+    }
     return 0;
 }
 
@@ -618,6 +626,8 @@ set_pwd_env(void)
 {
     Param pm;
 
+    /* update the PWD and OLDPWD shell parameters */
+
     pm = (Param) paramtab->getnode(paramtab, "PWD");
     if (pm && PM_TYPE(pm->flags) != PM_SCALAR) {
 	pm->flags &= ~PM_READONLY;
@@ -634,17 +644,22 @@ set_pwd_env(void)
     setsparam("OLDPWD", ztrdup(oldpwd));
 
     pm = (Param) paramtab->getnode(paramtab, "PWD");
-    if (!(pm->flags & PM_EXPORTED)) {
+    if (!(pm->flags & PM_EXPORTED) &&
+	(!pm->level || (isset(ALLEXPORT) && !pm->old))) {
 	pm->flags |= PM_EXPORTED;
-	pm->env = addenv("PWD", pwd);
+	pm->env = addenv("PWD", pwd, pm->flags);
     }
     pm = (Param) paramtab->getnode(paramtab, "OLDPWD");
-    if (!(pm->flags & PM_EXPORTED)) {
+    if (!(pm->flags & PM_EXPORTED) &&
+	(!pm->level || (isset(ALLEXPORT) && !pm->old))) {
 	pm->flags |= PM_EXPORTED;
-	pm->env = addenv("PWD", pwd);
+	pm->env = addenv("OLDPWD", oldpwd, pm->flags);
     }
 }
 
+/* set if we are resolving links to their true paths */
+static int chasinglinks;
+
 /* The main pwd changing function.  The real work is done by other     *
  * functions.  cd_get_dest() does the initial argument processing;     *
  * cd_do_chdir() actually changes directory, if possible; cd_new_pwd() *
@@ -657,7 +672,6 @@ bin_cd(char *nam, char **argv, char *ops, int func)
 {
     LinkNode dir;
     struct stat st1, st2;
-    int chaselinks;
 
     if (isset(RESTRICTED)) {
 	zwarnnam(nam, "restricted", NULL, 0);
@@ -678,33 +692,32 @@ bin_cd(char *nam, char **argv, char *ops, int func)
 		goto brk;
 	    }
 	} while (*++s);
-	for (s = *argv; *++s; ops[*s] = 1);
+	for (s = *argv; *++s; ops[STOUC(*s)] = 1);
     }
   brk:
-    chaselinks = ops['P'] || (isset(CHASELINKS) && !ops['L']);
-    PERMALLOC {
-	pushnode(dirstack, ztrdup(pwd));
-	if (!(dir = cd_get_dest(nam, argv, ops, func))) {
-	    zsfree(getlinknode(dirstack));
-	    LASTALLOC_RETURN 1;
-	}
-    } LASTALLOC;
-    cd_new_pwd(func, dir, chaselinks);
+    chasinglinks = ops['P'] || (isset(CHASELINKS) && !ops['L']);
+    zpushnode(dirstack, ztrdup(pwd));
+    if (!(dir = cd_get_dest(nam, argv, ops, func))) {
+	zsfree(getlinknode(dirstack));
+	return 1;
+    }
+    cd_new_pwd(func, dir);
 
     if (stat(unmeta(pwd), &st1) < 0) {
+	setjobpwd();
 	zsfree(pwd);
 	pwd = metafy(zgetcwd(), -1, META_DUP);
     } else if (stat(".", &st2) < 0)
 	chdir(unmeta(pwd));
     else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
-	if (chaselinks) {
+	if (chasinglinks) {
+	    setjobpwd();
 	    zsfree(pwd);
 	    pwd = metafy(zgetcwd(), -1, META_DUP);
 	} else {
 	    chdir(unmeta(pwd));
 	}
     }
-    set_pwd_env();
     return 0;
 }
 
@@ -726,9 +739,9 @@ cd_get_dest(char *nam, char **argv, char *ops, int func)
 	if (func == BIN_PUSHD && unset(PUSHDTOHOME))
 	    dir = nextnode(firstnode(dirstack));
 	if (dir)
-	    insertlinknode(dirstack, dir, getlinknode(dirstack));
+	    zinsertlinknode(dirstack, dir, getlinknode(dirstack));
 	else if (func != BIN_POPD)
-	    pushnode(dirstack, ztrdup(home));
+	    zpushnode(dirstack, ztrdup(home));
     } else if (!argv[1]) {
 	int dd;
 	char *end;
@@ -749,8 +762,8 @@ cd_get_dest(char *nam, char **argv, char *ops, int func)
 	    }
 	}
 	if (!dir)
-	    pushnode(dirstack, ztrdup(strcmp(argv[0], "-")
-				      ? (doprintdir--, argv[0]) : oldpwd));
+	    zpushnode(dirstack, ztrdup(strcmp(argv[0], "-")
+				       ? (doprintdir--, argv[0]) : oldpwd));
     } else {
 	char *u, *d;
 	int len1, len2, len3;
@@ -766,7 +779,7 @@ cd_get_dest(char *nam, char **argv, char *ops, int func)
 	strncpy(d, pwd, len3);
 	strcpy(d + len3, argv[1]);
 	strcat(d, u + len1);
-	pushnode(dirstack, d);
+	zpushnode(dirstack, d);
 	doprintdir++;
     }
 
@@ -789,7 +802,7 @@ cd_get_dest(char *nam, char **argv, char *ops, int func)
 	    zsfree(remnode(dirstack, dir));
 	return NULL;
     }
-    if (dest != getdata(dir)) {
+    if (dest != (char *)getdata(dir)) {
 	zsfree(getdata(dir));
 	setdata(dir, dest);
     }
@@ -903,40 +916,54 @@ static char *
 cd_try_chdir(char *pfix, char *dest, int hard)
 {
     char *buf;
+    int dlen, dochaselinks = 0;
 
     /* handle directory prefix */
     if (pfix && *pfix) {
 	if (*pfix == '/')
 	    buf = tricat(pfix, "/", dest);
 	else {
-	    int pwl = strlen(pwd);
 	    int pfl = strlen(pfix);
+	    dlen = strlen(pwd);
 
-	    buf = zalloc(pwl + pfl + strlen(dest) + 3);
+	    buf = zalloc(dlen + pfl + strlen(dest) + 3);
 	    strcpy(buf, pwd);
-	    buf[pwl] = '/';
-	    strcpy(buf + pwl + 1, pfix);
-	    buf[pwl + 1 + pfl] = '/';
-	    strcpy(buf + pwl + pfl + 2, dest);
+	    buf[dlen] = '/';
+	    strcpy(buf + dlen + 1, pfix);
+	    buf[dlen + 1 + pfl] = '/';
+	    strcpy(buf + dlen + pfl + 2, dest);
 	}
     } else if (*dest == '/')
 	buf = ztrdup(dest);
     else {
-	int pwl = strlen(pwd);
-
-	buf = zalloc(pwl + strlen(dest) + 2);
+	dlen = strlen(pwd);
+	if (pwd[dlen-1] == '/')
+	    --dlen;
+	buf = zalloc(dlen + strlen(dest) + 2);
 	strcpy(buf, pwd);
-	buf[pwl] = '/';
-	strcpy(buf + pwl + 1, dest);
+	buf[dlen] = '/';
+	strcpy(buf + dlen + 1, dest);
     }
 
-    /* Normalise path.  See the definition of fixdir() for what this means. */
-    fixdir(buf);
-
-    if (lchdir(buf, NULL, hard)) {
-	zsfree(buf);
+    /* Normalise path.  See the definition of fixdir() for what this means.
+     * We do not do this if we are chasing links.
+     */
+    if (!chasinglinks)
+	dochaselinks = fixdir(buf);
+    else
+	unmetafy(buf, &dlen);
+
+    /* We try the full path first.  If that fails, try the
+     * argument to cd relatively.  This is useful if the cwd
+     * or a parent directory is renamed in the interim.
+     */
+    if (lchdir(buf, NULL, hard) && lchdir(dest, NULL, hard)) {
+	free(buf);
 	return NULL;
     }
+    /* the chdir succeeded, so decide if we should force links to be chased */
+    if (dochaselinks)
+	chasinglinks = 1;
     return metafy(buf, -1, META_NOALLOC);
 }
 
@@ -944,10 +971,9 @@ cd_try_chdir(char *pfix, char *dest, int hard)
 
 /**/
 static void
-cd_new_pwd(int func, LinkNode dir, int chaselinks)
+cd_new_pwd(int func, LinkNode dir)
 {
-    Param pm;
-    List l;
+    Eprog prog;
     char *new_pwd, *s;
     int dirstacksize;
 
@@ -961,7 +987,7 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks)
     } else if (func == BIN_CD && unset(AUTOPUSHD))
 	zsfree(getlinknode(dirstack));
 
-    if (chaselinks) {
+    if (chasinglinks) {
 	s = new_pwd;
 	new_pwd = findpwd(s);
 	zsfree(s);
@@ -980,14 +1006,10 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks)
     current (i.e. new) pwd */
     zsfree(oldpwd);
     oldpwd = pwd;
+    setjobpwd();
     pwd = new_pwd;
-    /* update the PWD and OLDPWD shell parameters */
-    if ((pm = (Param) paramtab->getnode(paramtab, "PWD")) &&
-	(pm->flags & PM_EXPORTED) && pm->env)
-	pm->env = replenv(pm->env, pwd);
-    if ((pm = (Param) paramtab->getnode(paramtab, "OLDPWD")) &&
-	(pm->flags & PM_EXPORTED) && pm->env)
-	pm->env = replenv(pm->env, oldpwd);
+    set_pwd_env();
+
     if (unset(PUSHDSILENT) && func != BIN_CD && isset(INTERACTIVE))
 	printdirstack();
     else if (doprintdir) {
@@ -996,10 +1018,14 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks)
     }
 
     /* execute the chpwd function */
-    if ((l = getshfunc("chpwd")) != &dummy_list) {
+    if ((prog = getshfunc("chpwd")) != &dummy_eprog) {
+	int osc = sfcontext;
+
 	fflush(stdout);
 	fflush(stderr);
-	doshfunc(l, NULL, 0, 1);
+	sfcontext = SFC_HOOK;
+	doshfunc("chpwd", prog, NULL, 0, 1);
+	sfcontext = osc;
     }
 
     dirstacksize = getiparam("DIRSTACKSIZE");
@@ -1029,14 +1055,20 @@ printdirstack(void)
 }
 
 /* Normalise a path.  Segments consisting of ., and foo/.. *
- * combinations, are removed and the path is unmetafied.   */
+ * combinations, are removed and the path is unmetafied.
+ * Returns 1 if we found a ../ path which should force links to
+ * be chased, 0 otherwise.
+ */
 
 /**/
-static void
+int
 fixdir(char *src)
 {
-    char *dest = src;
-    char *d0 = dest;
+    char *dest = src, *d0 = dest;
+#ifdef __CYGWIN__
+    char *s0 = src;
+#endif
+    int ret = 0;
 
 /*** if have RFS superroot directory ***/
 #ifdef HAVE_SUPERROOT
@@ -1053,6 +1085,11 @@ fixdir(char *src)
     for (;;) {
 	/* compress multiple /es into single */
 	if (*src == '/') {
+#ifdef __CYGWIN__
+	    /* allow leading // under cygwin */
+	    if (src == s0 && src[1] == '/')
+		*dest++ = *src++;
+#endif
 	    *dest++ = *src++;
 	    while (*src == '/')
 		src++;
@@ -1063,17 +1100,40 @@ fixdir(char *src)
 	    while (dest > d0 + 1 && dest[-1] == '/')
 		dest--;
 	    *dest = '\0';
-	    return;
+	    return ret;
 	}
-	if (dest > d0 + 1 && src[0] == '.' && src[1] == '.' &&
-	  (src[2] == '\0' || src[2] == '/')) {
-	    /* remove a foo/.. combination */
-	    for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
-	    if (dest[-1] != '/')
-		dest--;
-	    src++;
-	    while (*++src == '/');
-	} else if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
+	if (src[0] == '.' && src[1] == '.' &&
+	    (src[2] == '\0' || src[2] == '/')) {
+	    if (isset(CHASEDOTS)) {
+		ret = 1;
+		/* and treat as normal path segment */
+	    } else {
+		if (dest > d0 + 1) {
+		    /*
+		     * remove a foo/.. combination:
+		     * first check foo exists, else return.
+		     */
+		    struct stat st;
+		    *dest = '\0';
+		    if (stat(d0, &st) < 0 || !S_ISDIR(st.st_mode)) {
+			char *ptrd, *ptrs;
+			if (dest == src)
+			    *dest = '.';
+			for (ptrs = src, ptrd = dest; *ptrs; ptrs++, ptrd++)
+			    *ptrd = (*ptrs == Meta) ? (*++ptrs ^ 32) : *ptrs;
+			*ptrd = '\0';
+			return 1;
+		    }
+		    for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
+		    if (dest[-1] != '/')
+			dest--;
+		}
+		src++;
+		while (*++src == '/');
+		continue;
+	    }
+	}
+	if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
 	    /* skip a . section */
 	    while (*++src == '/');
 	} else {
@@ -1086,7 +1146,7 @@ fixdir(char *src)
 }
 
 /**/
-void
+mod_export void
 printqt(char *str)
 {
     /* Print str, but turn any single quote into '\'' or ''. */
@@ -1098,7 +1158,7 @@ printqt(char *str)
 }
 
 /**/
-void
+mod_export void
 printif(char *str, int c)
 {
     /* If flag c has an argument, print that */
@@ -1119,7 +1179,7 @@ bin_fc(char *nam, char **argv, char *ops, int func)
     int first = -1, last = -1, retval, minflag = 0;
     char *s;
     struct asgment *asgf = NULL, *asgl = NULL;
-    Comp com = NULL;
+    Patprog pprog = NULL;
 
     /* fc is only permitted in interactive shells */
     if (!interact) {
@@ -1130,33 +1190,31 @@ bin_fc(char *nam, char **argv, char *ops, int func)
      * as a pattern that history lines have to match   */
     if (*argv && ops['m']) {
 	tokenize(*argv);
-	if (!(com = parsereg(*argv++))) {
+	if (!(pprog = patcompile(*argv++, 0, NULL))) {
 	    zwarnnam(nam, "invalid match pattern", NULL, 0);
 	    return 1;
 	}
     }
     if (ops['R']) {
 	/* read history from a file */
-	readhistfile(*argv ? *argv : getsparam("HISTFILE"), 1);
+	readhistfile(*argv, 1, ops['I'] ? HFILE_SKIPOLD : 0);
 	return 0;
     }
     if (ops['W']) {
 	/* write history to a file */
-	savehistfile(*argv ? *argv : getsparam("HISTFILE"), 1,
-		     (ops['I'] ? 2 : 0));
+	savehistfile(*argv, 1, ops['I'] ? HFILE_SKIPOLD : 0);
 	return 0;
     }
     if (ops['A']) {
 	/* append history to a file */
-	savehistfile(*argv ? *argv : getsparam("HISTFILE"), 1,
-		     (ops['I'] ? 3 : 1));
+	savehistfile(*argv, 1, HFILE_APPEND | (ops['I'] ? HFILE_SKIPOLD : 0));
 	return 0;
     }
     if (!(ops['l'] && unset(HISTNOSTORE)))
 	remhist();
     /* put foo=bar type arguments into the substitution list */
     while (*argv && equalsplit(*argv, &s)) {
-	Asgment a = (Asgment) alloc(sizeof *a);
+	Asgment a = (Asgment) zhalloc(sizeof *a);
 
 	if (!asgf)
 	    asgf = asgl = a;
@@ -1191,9 +1249,9 @@ bin_fc(char *nam, char **argv, char *ops, int func)
     }
     /* default values of first and last, and range checking */
     if (first == -1)
-	first = (ops['l']) ? curhist - 16 : curhist - 1;
+	first = ops['l']? addhistnum(curhist,-16,0) : addhistnum(curhist,-1,0);
     if (last == -1)
-	last = (ops['l']) ? curhist - 1 : first;
+	last = ops['l']? addhistnum(curhist,-1,0) : first;
     if (first < firsthist())
 	first = firsthist();
     if (last == -1)
@@ -1204,7 +1262,7 @@ bin_fc(char *nam, char **argv, char *ops, int func)
 	/* list the required part of the history */
 	retval = fclist(stdout, !ops['n'], ops['r'], ops['D'],
 			ops['d'] + ops['f'] * 2 + ops['E'] * 4 + ops['i'] * 8,
-			first, last, asgf, com);
+			first, last, asgf, pprog);
     else {
 	/* edit history file, and (if successful) use the result as a new command */
 	int tempfd;
@@ -1218,20 +1276,21 @@ bin_fc(char *nam, char **argv, char *ops, int func)
 		((out = fdopen(tempfd, "w")) == NULL)) {
 	    zwarnnam("fc", "can't open temp file: %e", NULL, errno);
 	} else {
-	    if (!fclist(out, 0, ops['r'], 0, 0, first, last, asgf, com)) {
+	    if (!fclist(out, 0, ops['r'], 0, 0, first, last, asgf, pprog)) {
 		char *editor;
 
 		editor = auxdata ? auxdata : getsparam("FCEDIT");
 		if (!editor)
 		    editor = DEFAULT_FCEDIT;
 
-		if (fcedit(editor, fil))
+		if (fcedit(editor, fil)) {
 		    if (stuff(fil))
 			zwarnnam("fc", "%e: %s", s, errno);
 		    else {
 			loop(0,1);
 			retval = lastval;
 		    }
+		}
 	    }
 	}
 	unlink(fil);
@@ -1257,7 +1316,7 @@ fcgetcomm(char *s)
      * numbers indicate reversed numbering.           */
     if ((cmd = atoi(s))) {
 	if (cmd < 0)
-	    cmd = curhist + cmd;
+	    cmd = addhistnum(curhist,cmd,HIST_FOREIGN);
 	if (cmd >= curhist) {
 	    zwarnnam("fc", "bad history number: %d", 0, cmd);
 	    return -1;
@@ -1289,7 +1348,7 @@ fcsubs(char **sp, struct asgment *sub)
 	oldpos = s;
 	/* loop over occurences of oldstr in s, replacing them with newstr */
 	while ((newpos = (char *)strstr(oldpos, oldstr))) {
-	    newmem = (char *) alloc(1 + (newpos - s)
+	    newmem = (char *) zhalloc(1 + (newpos - s)
 			+ strlen(newstr) + strlen(newpos + strlen(oldstr)));
 	    ztrncpy(newmem, s, newpos - s);
 	    strcat(newmem, newstr);
@@ -1318,10 +1377,10 @@ fcsubs(char **sp, struct asgment *sub)
 
 /**/
 static int
-fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Comp com)
+fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Patprog pprog)
 {
     int fclistdone = 0;
-    char *s, *hs;
+    char *s;
     Histent ent;
 
     /* reverse range if required */
@@ -1334,28 +1393,31 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
     if (!subs)
 	fclistdone = 1;
 
-    for (;;) {
-	hs = quietgetevent(first);
-	if (!hs) {
+    ent = gethistent(first, first < last? GETHIST_DOWNWARD : GETHIST_UPWARD);
+    if (!ent || (first < last? ent->histnum > last : ent->histnum < last)) {
+	if (first == last)
 	    zwarnnam("fc", "no such event: %d", NULL, first);
-	    return 1;
-	}
-	s = dupstring(hs);
+	else
+	    zwarnnam("fc", "no events in that range", NULL, 0);
+	return 1;
+    }
+
+    for (;;) {
+	s = dupstring(ent->text);
 	/* this if does the pattern matching, if required */
-	if (!com || domatch(s, com, 0)) {
+	if (!pprog || pattry(pprog, s)) {
 	    /* perform substitution */
 	    fclistdone |= fcsubs(&s, subs);
 
 	    /* do numbering */
-	    if (n)
-		fprintf(f, "%5d  ", first);
-	    ent = NULL;
+	    if (n) {
+		fprintf(f, "%5d%c ", ent->histnum,
+			ent->flags & HIST_FOREIGN? '*' : ' ');
+	    }
 	    /* output actual time (and possibly date) of execution of the
 	    command, if required */
 	    if (d) {
 		struct tm *ltm;
-		if (!ent)
-		    ent = gethistent(first);
 		ltm = localtime(&ent->stim);
 		if (d >= 2) {
 		    if (d >= 8) {
@@ -1377,8 +1439,6 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
 	    /* display the time taken by the command, if required */
 	    if (D) {
 		long diff;
-		if (!ent)
-		    ent = gethistent(first);
 		diff = (ent->ftim) ? ent->ftim - ent->stim : 0;
 		fprintf(f, "%ld:%02ld  ", diff / 60, diff % 60);
 	    }
@@ -1391,12 +1451,14 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
 		fprintf(f, "%s\n", s);
 	}
 	/* move on to the next history line, or quit the loop */
-	if (first == last)
-	    break;
-	else if (first > last)
-	    first--;
-	else
-	    first++;
+	if (first < last) {
+	    if (!(ent = down_histent(ent)) || ent->histnum > last)
+		break;
+	}
+	else {
+	    if (!(ent = up_histent(ent)) || ent->histnum < last)
+		break;
+	}
     }
 
     /* final processing */
@@ -1465,6 +1527,265 @@ getasg(char *s)
     return &asg;
 }
 
+/* function to set a single parameter */
+
+/**/
+Param
+typeset_single(char *cname, char *pname, Param pm, int func,
+	       int on, int off, int roff, char *value, Param altpm)
+{
+    int usepm, tc, keeplocal = 0, newspecial = 0;
+
+    /*
+     * Do we use the existing pm?  Note that this isn't the end of the
+     * story, because if we try and create a new pm at the same
+     * locallevel as an unset one we use the pm struct anyway: that's
+     * handled in createparam().  Here we just avoid using it for the
+     * present tests if it's unset.
+     */
+    usepm = pm && !(pm->flags & PM_UNSET);
+
+    /*
+     * We need to compare types with an existing pm if special,
+     * even if that's unset
+     */
+    if (pm && (pm->flags & PM_SPECIAL))
+	usepm = 1;
+
+    /*
+     * Don't use an existing param if
+     *   - the local level has changed, and
+     *   - we are really locallizing the parameter
+     */
+    if (usepm && locallevel != pm->level && (on & PM_LOCAL)) {
+	/*
+	 * If the original parameter was special and we're creating
+	 * a new one, we need to keep it special.
+	 *
+	 * The -h (hide) flags prevents an existing special being made
+	 * local.  It can be applied either to the special or in the
+	 * typeset/local statement for the local variable.
+	 */
+	newspecial = (pm->flags & PM_SPECIAL)
+	    && !(on & PM_HIDE) && !(pm->flags & PM_HIDE & ~off);
+	usepm = 0;
+    }
+
+    /* attempting a type conversion, or making a tied colonarray? */
+    tc = 0;
+    if (usepm || newspecial) {
+	int chflags = ((off & pm->flags) | (on & ~pm->flags)) &
+	     (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
+	      PM_ARRAY|PM_TIED|PM_AUTOLOAD);
+	/* keep the parameter if just switching between floating types */
+	if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT)))
+	    usepm = 0;
+    }
+    if (tc && (pm->flags & PM_SPECIAL)) {
+	zerrnam(cname, "%s: can't change type of a special parameter",
+		pname, 0);
+	return NULL;
+    }
+
+    /*
+     * According to the manual, local parameters don't get exported.
+     * A parameter will be local if
+     * 1. we are re-using an existing local parameter
+     *    or
+     * 2. we are not using an existing parameter, but
+     *   i. there is already a parameter, which will be hidden
+     *     or
+     *   ii. we are creating a new local parameter
+     */
+    if ((usepm && pm->level) ||
+	(!usepm && (pm || (locallevel && (on & PM_LOCAL)))))
+	on &= ~PM_EXPORTED;
+
+    if (usepm) {
+	on &= ~PM_LOCAL;
+	if (!on && !roff && !value) {
+	    paramtab->printnode((HashNode)pm, 0);
+	    return pm;
+	}
+	if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
+	    zerrnam(cname, "%s: restricted", pname, 0);
+	    return pm;
+	}
+	if ((on & PM_UNIQUE) && !(pm->flags & PM_READONLY & ~off)) {
+	    Param apm;
+	    if (PM_TYPE(pm->flags) == PM_ARRAY)
+		uniqarray((*pm->gets.afn) (pm));
+	    else if (PM_TYPE(pm->flags) == PM_SCALAR && pm->ename &&
+		     (apm = (Param) paramtab->getnode(paramtab, pm->ename)))
+		uniqarray((*apm->gets.afn) (apm));
+	}
+	pm->flags = (pm->flags | on) & ~(off | PM_UNSET);
+	/* This auxlen/pm->ct stuff is a nasty hack. */
+	if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER |
+		   PM_EFLOAT | PM_FFLOAT)) &&
+	    auxlen)
+	    pm->ct = auxlen;
+	if (!(pm->flags & (PM_ARRAY|PM_HASHED))) {
+	    if (pm->flags & PM_EXPORTED) {
+		if (!(pm->flags & PM_UNSET) && !pm->env && !value)
+		    pm->env = addenv(pname, getsparam(pname), pm->flags);
+	    } else if (pm->env &&
+		       (!pm->level || (isset(ALLEXPORT) && !pm->old))) {
+		delenv(pm->env);
+		zsfree(pm->env);
+		pm->env = NULL;
+	    }
+	    if (value)
+		setsparam(pname, ztrdup(value));
+	} else if (value) {
+	    zwarnnam(cname, "can't assign new value for array %s", pname, 0);
+	    return NULL;
+	}
+	return pm;
+    }
+
+    /*
+     * We're here either because we're creating a new parameter,
+     * or we're adding a parameter at a different local level,
+     * or we're converting the type of a parameter.  In the
+     * last case only, we need to delete the old parameter.
+     */
+    if (tc) {
+	/* Maintain existing readonly/exported status... */
+	on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->flags;
+	/* ...but turn off existing readonly so we can delete it */
+	pm->flags &= ~PM_READONLY;
+	/*
+	 * If we're just changing the type, we should keep the
+	 * variable at the current level of localness.
+	 */
+	keeplocal = pm->level;
+	/*
+	 * Try to carry over a value, but not when changing from,
+	 * to, or between non-scalar types.
+	 */
+	if (!value && !((pm->flags|on) & (PM_ARRAY|PM_HASHED)))
+	    value = dupstring(getsparam(pname));
+	/* pname may point to pm->nam which is about to disappear */
+	pname = dupstring(pname);
+	unsetparam_pm(pm, 0, 1);
+    }
+
+    if (newspecial) {
+	Param tpm, pm2;
+	if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
+	    zerrnam(cname, "%s: restricted", pname, 0);
+	    return pm;
+	}
+	/*
+	 * For specials, we keep the same struct but zero everything.
+	 * Maybe it would be easier to create a new struct but copy
+	 * the get/set methods.
+	 */
+	tpm = (Param) zalloc(sizeof *tpm);
+
+	tpm->nam = pm->nam;
+	if (pm->ename &&
+	    (pm2 = (Param) paramtab->getnode(paramtab, pm->ename)) &&
+	    pm2->level == locallevel) {
+	    /* This is getting silly, but anyway:  if one of a path/PATH
+	     * pair has already been made local at the current level, we
+	     * have to make sure that the other one does not have its value
+	     * saved:  since that comes from an internal variable it will
+	     * already reflect the local value, so restoring it on exit
+	     * would be wrong.
+	     *
+	     * This problem is also why we make sure we have a copy
+	     * of the environment entry in tpm->env, rather than relying
+	     * on the restored value to provide it.
+	     */
+	    tpm->flags = pm->flags | PM_NORESTORE;
+	} else {
+	    copyparam(tpm, pm, 1);
+	}
+	tpm->old = pm->old;
+	tpm->level = pm->level;
+	tpm->ct = pm->ct;
+	tpm->env = pm->env;
+
+	pm->old = tpm;
+	/*
+	 * The remaining on/off flags should be harmless to use,
+	 * because we've checked for unpleasant surprises above.
+	 */
+	pm->flags = (PM_TYPE(pm->flags) | on | PM_SPECIAL) & ~off;
+	/*
+	 * Final tweak: if we've turned on one of the flags with
+	 * numbers, we should use the appropriate integer.
+	 */
+	if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z|PM_INTEGER|
+		  PM_EFLOAT|PM_FFLOAT))
+	    pm->ct = auxlen;
+	else
+	    pm->ct = 0;
+	pm->env = NULL;
+    } else {
+	/*
+	 * Create a new node for a parameter with the flags in `on' minus the
+	 * readonly flag
+	 */
+	pm = createparam(pname, on & ~PM_READONLY);
+	DPUTS(!pm, "BUG: parameter not created");
+	pm->ct = auxlen;
+    }
+
+    if (altpm && PM_TYPE(pm->flags) == PM_SCALAR) {
+	/*
+	 * It seems safer to set this here than in createparam(),
+	 * to make sure we only ever use the colonarr functions
+	 * when u.data is correctly set.
+	 */
+	pm->sets.cfn = colonarrsetfn;
+	pm->gets.cfn = colonarrgetfn;
+	pm->u.data = &altpm->u.arr;
+    }
+
+    if (keeplocal)
+	pm->level = keeplocal;
+    else if (on & PM_LOCAL)
+	pm->level = locallevel;
+    if (value && !(pm->flags & (PM_ARRAY|PM_HASHED)))
+	setsparam(pname, ztrdup(value));
+    else if (newspecial && !(pm->old->flags & PM_NORESTORE)) {
+	/*
+	 * We need to use the special setting function to re-initialise
+	 * the special parameter to empty.
+	 */
+	switch (PM_TYPE(pm->flags)) {
+	case PM_SCALAR:
+	    pm->sets.cfn(pm, ztrdup(""));
+	    break;
+	case PM_INTEGER:
+	    pm->sets.ifn(pm, 0);
+	    break;
+	case PM_EFLOAT:
+	case PM_FFLOAT:
+	    pm->sets.ffn(pm, 0.0);
+	    break;
+	case PM_ARRAY:
+	    pm->sets.afn(pm, mkarray(NULL));
+	    break;
+	case PM_HASHED:
+	    pm->sets.hfn(pm, newparamtable(17, pm->nam));
+	    break;
+	}
+    }
+    pm->flags |= (on & PM_READONLY);
+    if (value && (pm->flags & (PM_ARRAY|PM_HASHED))) {
+	zerrnam(cname, "%s: can't assign initial value for array", pname, 0);
+	/* the only safe thing to do here seems to be unset the param */
+	unsetparam_pm(pm, 0, 1);
+	return NULL;
+    }
+
+    return pm;
+}
+
 /* declare, export, integer, local, readonly, typeset */
 
 /**/
@@ -1473,10 +1794,10 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 {
     Param pm;
     Asgment asg;
-    Comp com;
-    char *optstr = "iLRZlurtxU";
-    int on = 0, off = 0, roff, bit = PM_INTEGER;
-    int initon, initoff, of, i;
+    Patprog pprog;
+    char *optstr = TYPESET_OPTSTR;
+    int on = 0, off = 0, roff, bit = PM_ARRAY;
+    int i;
     int returnval = 0, printflags = 0;
 
     /* hash -f is really the builtin `functions' */
@@ -1487,25 +1808,40 @@ bin_typeset(char *name, char **argv, char *ops, int func)
      * Unfortunately, this depends on the order *
      * these flags are defined in zsh.h         */
     for (; *optstr; optstr++, bit <<= 1)
-	if (ops[*optstr] == 1)
+	if (ops[STOUC(*optstr)] == 1)
 	    on |= bit;
-	else if (ops[*optstr] == 2)
+	else if (ops[STOUC(*optstr)] == 2)
 	    off |= bit;
     roff = off;
 
     /* Sanity checks on the options.  Remove conficting options. */
+    if (on & PM_FFLOAT) {
+	off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY |
+	    PM_HASHED | PM_INTEGER | PM_EFLOAT;
+	/* Allow `float -F' to work even though float sets -E by default */
+	on &= ~PM_EFLOAT;
+    }
+    if (on & PM_EFLOAT)
+	off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY |
+	    PM_HASHED | PM_INTEGER | PM_FFLOAT;
     if (on & PM_INTEGER)
-	off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY;
+	off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY |
+	    PM_HASHED | PM_EFLOAT | PM_FFLOAT;
     if (on & PM_LEFT)
-	off |= PM_RIGHT_B | PM_INTEGER;
+	off |= PM_RIGHT_B | PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
     if (on & PM_RIGHT_B)
-	off |= PM_LEFT | PM_INTEGER;
+	off |= PM_LEFT | PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
     if (on & PM_RIGHT_Z)
-	off |= PM_INTEGER;
+	off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT;
     if (on & PM_UPPER)
 	off |= PM_LOWER;
     if (on & PM_LOWER)
 	off |= PM_UPPER;
+    if (on & PM_HASHED)
+	off |= PM_ARRAY;
+    if (on & PM_TIED)
+	off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED;
+
     on &= ~off;
 
     /* Given no arguments, list whatever the options specify. */
@@ -1518,148 +1854,171 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	return 0;
     }
 
+    if (!ops['g'] && !ops['x'])
+	on |= PM_LOCAL;
+
+    if (on & PM_TIED) {
+	Param apm;
+	struct asgment asg0;
+	char *oldval = NULL;
+
+	if (ops['m']) {
+	    zwarnnam(name, "incompatible options for -T", NULL, 0);
+	    return 1;
+	}
+	on &= ~off;
+	if (!argv[1] || argv[2]) {
+	    zwarnnam(name, "-T requires names of scalar and array", NULL, 0);
+	    return 1;
+	}
+
+	if (!(asg = getasg(argv[0])))
+	    return 1;
+	asg0 = *asg;
+	if (!(asg = getasg(argv[1])))
+	    return 1;
+	if (!strcmp(asg0.name, asg->name)) {
+	    zerrnam(name, "can't tie a variable to itself", NULL, 0);
+	    return 1;
+	}
+	/*
+	 * Keep the old value of the scalar.  We need to do this
+	 * here as if it is already tied to the same array it
+	 * will be unset when we retie the array.  This is all
+	 * so that typeset -T is idempotent.
+	 *
+	 * We also need to remember here whether the damn thing is
+	 * exported and pass that along.  Isn't the world complicated?
+	 */
+	if ((pm = (Param) paramtab->getnode(paramtab, asg0.name))
+	    && !(pm->flags & PM_UNSET)
+	    && (locallevel == pm->level || !(on & PM_LOCAL))) {
+	    if (!asg0.value && !(PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED)))
+		oldval = ztrdup(getsparam(asg0.name));
+	    on |= (pm->flags & PM_EXPORTED);
+	}
+	/*
+	 * Create the tied array; this is normal except that
+	 * it has the PM_TIED flag set.  Do it first because
+	 * we need the address.
+	 */
+	if (!(apm=typeset_single(name, asg->name,
+				 (Param)paramtab->getnode(paramtab,
+							  asg->name),
+				 func, (on | PM_ARRAY) & ~PM_EXPORTED,
+				 off, roff, asg->value, NULL)))
+	    return 1;
+
+	/*
+	 * Create the tied colonarray.  We make it as a normal scalar
+	 * and fix up the oddities later.
+	 */
+	if (!(pm=typeset_single(name, asg0.name,
+				(Param)paramtab->getnode(paramtab,
+							 asg0.name),
+				func, on, off, roff, asg0.value, apm))) {
+	    if (oldval)
+		zsfree(oldval);
+	    unsetparam_pm(apm, 1, 1);
+	    return 1;
+	}
+
+	pm->ename = ztrdup(asg->name);
+	apm->ename = ztrdup(asg0.name);
+	if (oldval)
+	    setsparam(asg0.name, oldval);
+
+	return 0;
+    }
+    if (off & PM_TIED) {
+	zerrnam(name, "use unset to remove tied variables", NULL, 0);
+	return 1;
+    }
+
     /* With the -m option, treat arguments as glob patterns */
     if (ops['m']) {
 	while ((asg = getasg(*argv++))) {
+	    LinkList pmlist = newlinklist();
+	    LinkNode pmnode;
+
 	    tokenize(asg->name);   /* expand argument */
-	    if (!(com = parsereg(asg->name))) {
+	    if (!(pprog = patcompile(asg->name, 0, NULL))) {
 		untokenize(asg->name);
 		zwarnnam(name, "bad pattern : %s", argv[-1], 0);
 		returnval = 1;
 		continue;
 	    }
-	    /* If no options or values are given, display all *
-	     * parameters matching the glob pattern.          */
-	    if (!(on || roff || asg->value)) {
-		scanmatchtable(paramtab, com, 0, 0, paramtab->printnode, 0);
-		continue;
-	    }
-	    /* Since either options or values are given, we search   *
-	     * through the parameter table and change all parameters *
-	     * matching the glob pattern to have these flags and/or  *
-	     * value.                                                */
+	    /*
+	     * Search through the parameter table and change all parameters
+	     * matching the glob pattern to have these flags and/or value.
+	     * Bad news:  if the parameter gets altered, e.g. by
+	     * a type conversion, then paramtab can be shifted around,
+	     * so we need to store the parameters to alter on a separate
+	     * list for later use.	     
+	     */
 	    for (i = 0; i < paramtab->hsize; i++) {
-		for (pm = (Param) paramtab->nodes[i]; pm; pm = (Param) pm->next) {
-		    if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED))
+		for (pm = (Param) paramtab->nodes[i]; pm;
+		     pm = (Param) pm->next) {
+		    if (((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) ||
+			(pm->flags & PM_UNSET))
 			continue;
-		    if (domatch(pm->nam, com, 0)) {
-			/* set up flags if we have any */
-			if (on || roff) {
-			    if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
-				!(pm->flags & PM_READONLY & ~off))
-				uniqarray((*pm->gets.afn) (pm));
-			    pm->flags = (pm->flags | on) & ~off;
-			    if (PM_TYPE(pm->flags) != PM_ARRAY) {
-				if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && auxlen)
-				    pm->ct = auxlen;
-				/* did we just export this? */
-				if ((pm->flags & PM_EXPORTED) && !pm->env) {
-				    pm->env = addenv(pm->nam, (asg->value) ? asg->value : getsparam(pm->nam));
-				} else if (!(pm->flags & PM_EXPORTED) && pm->env) {
-				/* did we just unexport this? */
-				    delenv(pm->env);
-				    zsfree(pm->env);
-				    pm->env = NULL;
-				}
-			    }
-			}
-			/* set up a new value if given */
-			if (asg->value) {
-			    setsparam(pm->nam, ztrdup(asg->value));
-			}
-		    }
+		    if (pattry(pprog, pm->nam))
+			addlinknode(pmlist, pm);
 		}
 	    }
+	    for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
+		pm = (Param) getdata(pmnode);
+		if (!typeset_single(name, pm->nam, pm, func, on, off, roff,
+				    asg->value, NULL))
+		    returnval = 1;
+	    }
 	}
 	return returnval;
     }
 
-    /* Save the values of on, off, and func */
-    initon = on;
-    initoff = off;
-    of = func;
-
     /* Take arguments literally.  Don't glob */
     while ((asg = getasg(*argv++))) {
-	/* restore the original values of on, off, and func */
-	on = initon;
-	off = initoff;
-	func = of;
-	on &= ~PM_ARRAY;
-
 	/* check if argument is a valid identifier */
 	if (!isident(asg->name)) {
 	    zerr("not an identifier: %s", asg->name, 0);
 	    returnval = 1;
 	    continue;
 	}
-	bit = 0;    /* flag for switching int<->not-int */
-	if ((pm = (Param)paramtab->getnode(paramtab, asg->name)) &&
-	    (((pm->flags & PM_SPECIAL) && pm->level == locallevel) ||
-	     (!(pm->flags & PM_UNSET) &&
-	      ((locallevel == pm->level) || func == BIN_EXPORT) &&
-	      !(bit = ((off & pm->flags) | (on & ~pm->flags)) & PM_INTEGER)))) {
-	    /* if no flags or values are given, just print this parameter */
-	    if (!on && !roff && !asg->value) {
-		paramtab->printnode((HashNode) pm, 0);
-		continue;
-	    }
-	    if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
-		zerrnam(name, "%s: restricted", pm->nam, 0);
-		returnval = 1;
-		continue;
-	    }
-	    if((pm->flags & PM_SPECIAL) &&
-	       PM_TYPE((pm->flags | on) & ~off) != PM_TYPE(pm->flags)) {
-		zerrnam(name, "%s: cannot change type of a special parameter",
-		    pm->nam, 0);
-		returnval = 1;
-		continue;
-	    }
-	    if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
-		!(pm->flags & PM_READONLY & ~off))
-		uniqarray((*pm->gets.afn) (pm));
-	    pm->flags = (pm->flags | on) & ~off;
-	    if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) &&
-		auxlen)
-		pm->ct = auxlen;
-	    if (PM_TYPE(pm->flags) != PM_ARRAY) {
-		if (pm->flags & PM_EXPORTED) {
-		    if (!(pm->flags & PM_UNSET) && !pm->env && !asg->value)
-			pm->env = addenv(asg->name, getsparam(asg->name));
-		} else if (pm->env) {
-		    delenv(pm->env);
-		    zsfree(pm->env);
-		    pm->env = NULL;
-		}
-		if (asg->value)
-		    setsparam(asg->name, ztrdup(asg->value));
-	    }
-	} else {
-	    if (bit) {
-		if (pm->flags & PM_READONLY) {
-		    on |= ~off & PM_READONLY;
-		    pm->flags &= ~PM_READONLY;
-		}
-		if (!asg->value)
-		    asg->value = dupstring(getsparam(asg->name));
-		unsetparam(asg->name);
-	    }
-	    /* create a new node for a parameter with the *
-	     * flags in `on' minus the readonly flag      */
-	    pm = createparam(ztrdup(asg->name), on & ~PM_READONLY);
-	    DPUTS(!pm, "BUG: parameter not created");
-	    pm->ct = auxlen;
-	    if (func != BIN_EXPORT)
-		pm->level = locallevel;
-	    if (asg->value)
-		setsparam(asg->name, ztrdup(asg->value));
-	    pm->flags |= (on & PM_READONLY);
-	}
+	if (!typeset_single(name, asg->name,
+			    (Param) (paramtab == realparamtab ?
+				     gethashnode2(paramtab, asg->name) :
+				     paramtab->getnode(paramtab, asg->name)),
+			    func, on, off, roff, asg->value, NULL))
+	    returnval = 1;
     }
     return returnval;
 }
 
+/* Helper for bin_functions() when run as "autoload -X" */
+
+/**/
+int
+eval_autoload(Shfunc shf, char *name, char *ops, int func)
+{
+    if (!(shf->flags & PM_UNDEFINED))
+	return 1;
+
+    if (shf->funcdef) {
+	freeeprog(shf->funcdef);
+	shf->funcdef = &dummy_eprog;
+    }
+    if (ops['X'] == 1) {
+	char *fargv[3];
+	fargv[0] = name;
+	fargv[1] = "\"$@\"";
+	fargv[2] = 0;
+	shf->funcdef = mkautofn(shf);
+	return bin_eval(name, fargv, ops, func);
+    }
+
+    return !loadautofn(shf, (ops['k'] ? 2 : (ops['z'] ? 0 : 1)), 1);
+}
+
 /* Display or change the attributes of shell functions.   *
  * If called as autoload, it will define a new autoloaded *
  * (undefined) shell function.                            */
@@ -1668,34 +2027,55 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 int
 bin_functions(char *name, char **argv, char *ops, int func)
 {
-    Comp com;
+    Patprog pprog;
     Shfunc shf;
     int i, returnval = 0;
-    int on = 0, off = 0;
+    int on = 0, off = 0, pflags = 0;
 
     /* Do we have any flags defined? */
-    if (ops['u'] || ops['t']) {
-	if (ops['u'] == 1)
-	    on |= PM_UNDEFINED;
-	else if (ops['u'] == 2)
-	    off |= PM_UNDEFINED;
-
-	if (ops['t'] == 1)
-	    on |= PM_TAGGED;
-	else if (ops['t'] == 2)
-	    off |= PM_TAGGED;
-    }
-
-    if (off & PM_UNDEFINED) {
+    if (ops['u'] == 2)
+	off |= PM_UNDEFINED;
+    else if (ops['u'] == 1 || ops['X'])
+	on |= PM_UNDEFINED;
+    if (ops['U'] == 1)
+	on |= PM_UNALIASED|PM_UNDEFINED;
+    else if (ops['U'] == 2)
+	off |= PM_UNALIASED;
+    if (ops['t'] == 1)
+	on |= PM_TAGGED;
+    else if (ops['t'] == 2)
+	off |= PM_TAGGED;
+
+    if ((off & PM_UNDEFINED) || (ops['k'] && ops['z']) ||
+	(ops['X'] != 2 && (ops['k'] || ops['z'])) ||
+	(ops['X'] == 1 && (ops['m'] || *argv || !scriptname))) {
 	zwarnnam(name, "invalid option(s)", NULL, 0);
 	return 1;
     }
 
+    if (ops['f'] == 2 || ops['+'])
+	pflags |= PRINT_NAMEONLY;
+
     /* If no arguments given, we will print functions.  If flags *
      * are given, we will print only functions containing these  *
      * flags, else we'll print them all.                         */
     if (!*argv) {
-	scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode, 0);
+	if (ops['X'] == 1) {
+	    if ((shf = (Shfunc) shfunctab->getnode(shfunctab, scriptname))) {
+		DPUTS(!shf->funcdef,
+		      "BUG: Calling autoload from empty function");
+	    } else {
+		shf = (Shfunc) zcalloc(sizeof *shf);
+		shfunctab->addnode(shfunctab, ztrdup(scriptname), shf);
+	    }
+	    shf->flags = on;
+	    return eval_autoload(shf, scriptname, ops, func);
+	} else {
+	    if (ops['U'] && !ops['u'])
+		on &= ~PM_UNDEFINED;
+	    scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode,
+			  pflags);
+	}
 	return 0;
     }
 
@@ -1705,16 +2085,25 @@ bin_functions(char *name, char **argv, char *ops, int func)
 	for (; *argv; argv++) {
 	    /* expand argument */
 	    tokenize(*argv);
-	    if ((com = parsereg(*argv))) {
+	    if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
 		/* with no options, just print all functions matching the glob pattern */
 		if (!(on|off)) {
-		    scanmatchtable(shfunctab, com, 0, DISABLED, shfunctab->printnode, 0);
+		    scanmatchtable(shfunctab, pprog, 0, DISABLED,
+				   shfunctab->printnode, pflags);
 		} else {
 		/* apply the options to all functions matching the glob pattern */
 		    for (i = 0; i < shfunctab->hsize; i++) {
-			for (shf = (Shfunc) shfunctab->nodes[i]; shf; shf = (Shfunc) shf->next)
-			    if (domatch(shf->nam, com, 0) && !(shf->flags & DISABLED))
-				shf->flags = (shf->flags | on) & (~off);
+			for (shf = (Shfunc) shfunctab->nodes[i]; shf;
+			     shf = (Shfunc) shf->next)
+			    if (pattry(pprog, shf->nam) &&
+				!(shf->flags & DISABLED)) {
+				shf->flags = (shf->flags |
+					      (on & ~PM_UNDEFINED)) & ~off;
+				if (ops['X'] &&
+				    eval_autoload(shf, shf->nam, ops, func)) {
+				    returnval = 1;
+				}
+			    }
 		    }
 		}
 	    } else {
@@ -1728,14 +2117,21 @@ bin_functions(char *name, char **argv, char *ops, int func)
 
     /* Take the arguments literally -- do not glob */
     for (; *argv; argv++) {
-	if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) {
+	if (ops['w']) {
+	    if (dump_autoload(*argv, on, ops, func)) {
+		zwarnnam(name, "invalid wordcode file: %s", *argv, 0);
+		returnval = 1;
+	    }
+	} else if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) {
 	    /* if any flag was given */
-	    if (on|off)
+	    if (on|off) {
 		/* turn on/off the given flags */
 		shf->flags = (shf->flags | (on & ~PM_UNDEFINED)) & ~off;
-	    else
+		if (ops['X'] && eval_autoload(shf, shf->nam, ops, func))
+		    returnval = 1;
+	    } else
 		/* no flags, so just print */
-		shfunctab->printnode((HashNode) shf, 0);
+		shfunctab->printnode((HashNode) shf, pflags);
 	} else if (on & PM_UNDEFINED) {
 	    /* Add a new undefined (autoloaded) function to the *
 	     * hash table with the corresponding flags set.     */
@@ -1743,6 +2139,8 @@ bin_functions(char *name, char **argv, char *ops, int func)
 	    shf->flags = on;
 	    shf->funcdef = mkautofn(shf);
 	    shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
+	    if (ops['X'] && eval_autoload(shf, shf->nam, ops, func))
+		returnval = 1;
 	} else
 	    returnval = 1;
     }
@@ -1750,30 +2148,28 @@ bin_functions(char *name, char **argv, char *ops, int func)
 }
 
 /**/
-static List
+Eprog
 mkautofn(Shfunc shf)
 {
-    List l;
-    Sublist s;
-    Pline p;
-    Cmd c;
-    AutoFn a;
-    PERMALLOC {
-	a = (AutoFn)allocnode(N_AUTOFN);
-	a->shf = shf;
-	c = (Cmd)allocnode(N_CMD);
-	c->type = AUTOFN;
-	c->u.autofn = a;
-	p = (Pline)allocnode(N_PLINE);
-	p->left = c;
-	p->type = END;
-	s = (Sublist)allocnode(N_SUBLIST);
-	s->left = p;
-	l = (List)allocnode(N_LIST);
-	l->left = s;
-	l->type = Z_SYNC;
-    } LASTALLOC;
-    return l;
+    Eprog p;
+
+    p = (Eprog) zalloc(sizeof(*p));
+    p->len = 5 * sizeof(wordcode);
+    p->prog = (Wordcode) zalloc(p->len);
+    p->strs = NULL;
+    p->shf = shf;
+    p->npats = 0;
+    p->pats = NULL;
+    p->flags = EF_REAL;
+    p->dump = NULL;
+
+    p->prog[0] = WCB_LIST((Z_SYNC | Z_END), 0);
+    p->prog[1] = WCB_SUBLIST(WC_SUBLIST_END, 0, 3);
+    p->prog[2] = WCB_PIPE(WC_PIPE_END, 0);
+    p->prog[3] = WCB_AUTOFN();
+    p->prog[4] = WCB_END();
+
+    return p;
 }
 
 /* unset: unset parameters */
@@ -1783,7 +2179,7 @@ int
 bin_unset(char *name, char **argv, char *ops, int func)
 {
     Param pm, next;
-    Comp com;
+    Patprog pprog;
     char *s;
     int match = 0, returnval = 0;
     int i;
@@ -1797,15 +2193,16 @@ bin_unset(char *name, char **argv, char *ops, int func)
 	while ((s = *argv++)) {
 	    /* expand */
 	    tokenize(s);
-	    if ((com = parsereg(s))) {
+	    if ((pprog = patcompile(s, PAT_STATIC, NULL))) {
 		/* Go through the parameter table, and unset any matches */
 		for (i = 0; i < paramtab->hsize; i++) {
 		    for (pm = (Param) paramtab->nodes[i]; pm; pm = next) {
 			/* record pointer to next, since we may free this one */
 			next = (Param) pm->next;
 			if ((!(pm->flags & PM_RESTRICTED) ||
-			    unset(RESTRICTED)) && domatch(pm->nam, com, 0)) {
-			    unsetparam(pm->nam);
+			    unset(RESTRICTED)) &&
+			    pattry(pprog, pm->nam)) {
+			    unsetparam_pm(pm, 0, 1);
 			    match++;
 			}
 		    }
@@ -1824,14 +2221,41 @@ bin_unset(char *name, char **argv, char *ops, int func)
 
     /* do not glob -- unset the given parameter */
     while ((s = *argv++)) {
-	pm = (Param) paramtab->getnode(paramtab, s);
+	char *ss = strchr(s, '[');
+	char *sse = ss;
+	if (ss) {
+	    if (skipparens('[', ']', &sse) || *sse) {
+		zerrnam(name, "%s: invalid parameter name", s, 0);
+		returnval = 1;
+		continue;
+	    }
+	    *ss = 0;
+	}
+	pm = (Param) (paramtab == realparamtab ?
+		      gethashnode2(paramtab, s) :
+		      paramtab->getnode(paramtab, s));
 	if (!pm)
 	    returnval = 1;
 	else if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
 	    zerrnam(name, "%s: restricted", pm->nam, 0);
 	    returnval = 1;
+	} else if (ss) {
+	    if (PM_TYPE(pm->flags) == PM_HASHED) {
+		HashTable tht = paramtab;
+		if ((paramtab = pm->gets.hfn(pm))) {
+		    *--sse = 0;
+		    unsetparam(ss+1);
+		    *sse = ']';
+		}
+		paramtab = tht;
+	    } else {
+		zerrnam(name, "%s: invalid element for unset", s, 0);
+		returnval = 1;
+	    }
 	} else
-	    unsetparam(s);
+	    unsetparam_pm(pm, 0, 1);
+	if (ss)
+	    *ss = '[';
     }
     return returnval;
 }
@@ -1843,7 +2267,7 @@ int
 bin_whence(char *nam, char **argv, char *ops, int func)
 {
     HashNode hn;
-    Comp com;
+    Patprog pprog;
     int returnval = 0;
     int printflags = 0;
     int csh, all, v, wd;
@@ -1872,7 +2296,7 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 	for (; *argv; argv++) {
 	    /* parse the pattern */
 	    tokenize(*argv);
-	    if (!(com = parsereg(*argv))) {
+	    if (!(pprog = patcompile(*argv, PAT_STATIC, NULL))) {
 		untokenize(*argv);
 		zwarnnam(nam, "bad pattern : %s", *argv, 0);
 		returnval = 1;
@@ -1883,21 +2307,26 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 		 * We're not using it, so search for ... */
 
 		/* aliases ... */
-		scanmatchtable(aliastab, com, 0, DISABLED, aliastab->printnode, printflags);
+		scanmatchtable(aliastab, pprog, 0, DISABLED,
+			       aliastab->printnode, printflags);
 
 		/* and reserved words ... */
-		scanmatchtable(reswdtab, com, 0, DISABLED, reswdtab->printnode, printflags);
+		scanmatchtable(reswdtab, pprog, 0, DISABLED,
+			       reswdtab->printnode, printflags);
 
 		/* and shell functions... */
-		scanmatchtable(shfunctab, com, 0, DISABLED, shfunctab->printnode, printflags);
+		scanmatchtable(shfunctab, pprog, 0, DISABLED,
+			       shfunctab->printnode, printflags);
 
 		/* and builtins. */
-		scanmatchtable(builtintab, com, 0, DISABLED, builtintab->printnode, printflags);
+		scanmatchtable(builtintab, pprog, 0, DISABLED,
+			       builtintab->printnode, printflags);
 	    }
 	    /* Done search for `internal' commands, if the -p option *
 	     * was not used.  Now search the path.                   */
 	    cmdnamtab->filltable(cmdnamtab);
-	    scanmatchtable(cmdnamtab, com, 0, 0, cmdnamtab->printnode, printflags);
+	    scanmatchtable(cmdnamtab, pprog, 0, 0,
+			   cmdnamtab->printnode, printflags);
 
 	}
     return returnval;
@@ -1979,7 +2408,7 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 		puts(wd ? ": none" : " not found");
 		returnval = 1;
 	    }
-	} else if ((cnam = findcmd(*argv))) {
+	} else if ((cnam = findcmd(*argv, 1))) {
 	    /* Found external command. */
 	    if (wd) {
 		printf("%s: command\n", *argv);
@@ -1991,7 +2420,6 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 		    print_if_link(cnam);
 		fputc('\n', stdout);
 	    }
-	    zsfree(cnam);
 	} else {
 	    /* Not found at all. */
 	    if (v || csh || wd)
@@ -2027,7 +2455,7 @@ int
 bin_hash(char *name, char **argv, char *ops, int func)
 {
     HashTable ht;
-    Comp com;
+    Patprog pprog;
     Asgment asg;
     int returnval = 0;
 
@@ -2065,9 +2493,9 @@ bin_hash(char *name, char **argv, char *ops, int func)
 	if (ops['m']) {
 	    /* with the -m option, treat the argument as a glob pattern */
 	    tokenize(*argv);  /* expand */
-	    if ((com = parsereg(*argv))) {
+	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
 		/* display matching hash table elements */
-		scanmatchtable(ht, com, 0, 0, ht->printnode, 0);
+		scanmatchtable(ht, pprog, 0, 0, ht->printnode, 0);
 	    } else {
 		untokenize(*argv);
 		zwarnnam(name, "bad pattern : %s", *argv, 0);
@@ -2124,7 +2552,7 @@ bin_unhash(char *name, char **argv, char *ops, int func)
 {
     HashTable ht;
     HashNode hn, nhn;
-    Comp com;
+    Patprog pprog;
     int match = 0, returnval = 0;
     int i;
 
@@ -2144,13 +2572,13 @@ bin_unhash(char *name, char **argv, char *ops, int func)
 	for (; *argv; argv++) {
 	    /* expand argument */
 	    tokenize(*argv);
-	    if ((com = parsereg(*argv))) {
+	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
 		/* remove all nodes matching glob pattern */
 		for (i = 0; i < ht->hsize; i++) {
 		    for (hn = ht->nodes[i]; hn; hn = nhn) {
 			/* record pointer to next, since we may free this one */
 			nhn = hn->next;
-			if (domatch(hn->nam, com, 0)) {
+			if (pattry(pprog, hn->nam)) {
 			    ht->freenode(ht->removenode(ht, hn->nam));
 			    match++;
 			}
@@ -2189,7 +2617,7 @@ int
 bin_alias(char *name, char **argv, char *ops, int func)
 {
     Alias a;
-    Comp com;
+    Patprog pprog;
     Asgment asg;
     int haveflags = 0, returnval = 0;
     int flags1 = 0, flags2 = DISABLED;
@@ -2210,6 +2638,8 @@ bin_alias(char *name, char **argv, char *ops, int func)
 
     if (ops['L'])
 	printflags |= PRINT_LIST;
+    else if (ops['r'] == 2 || ops['g'] == 2 || ops['m'] == 2 || ops['+'])
+	printflags |= PRINT_NAMEONLY;
 
     /* In the absence of arguments, list all aliases.  If a command *
      * line flag is specified, list only those of that type.        */
@@ -2223,9 +2653,10 @@ bin_alias(char *name, char **argv, char *ops, int func)
     if (ops['m']) {
 	for (; *argv; argv++) {
 	    tokenize(*argv);  /* expand argument */
-	    if ((com = parsereg(*argv))) {
+	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
 		/* display the matching aliases */
-		scanmatchtable(aliastab, com, flags1, flags2, aliastab->printnode, printflags);
+		scanmatchtable(aliastab, pprog, flags1, flags2,
+			       aliastab->printnode, printflags);
 	    } else {
 		untokenize(*argv);
 		zwarnnam(name, "bad pattern : %s", *argv, 0);
@@ -2278,7 +2709,7 @@ bin_false(char *name, char **argv, char *ops, int func)
 /* the zle buffer stack */
  
 /**/
-LinkList bufstack;
+mod_export LinkList bufstack;
 
 /* echo, print, pushln */
 
@@ -2294,38 +2725,46 @@ bin_print(char *name, char **args, char *ops, int func)
     /* -m option -- treat the first argument as a pattern and remove
      * arguments not matching */
     if (ops['m']) {
-	Comp com;
+	Patprog pprog;
 	char **t, **p;
 
 	tokenize(*args);
-	if (!(com = parsereg(*args))) {
+	if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) {
 	    untokenize(*args);
 	    zwarnnam(name, "bad pattern : %s", *args, 0);
 	    return 1;
 	}
 	for (p = ++args; *p; p++)
-	    if (!domatch(*p, com, 0))
+	    if (!pattry(pprog, *p))
 		for (t = p--; (*t = t[1]); t++);
     }
     /* compute lengths, and interpret according to -P, -D, -e, etc. */
     argc = arrlen(args);
-    len = (int *)ncalloc(argc * sizeof(int));
+    len = (int *) hcalloc(argc * sizeof(int));
     for(n = 0; n < argc; n++) {
 	/* first \ sequences */
 	if (!ops['e'] && (ops['R'] || ops['r'] || ops['E']))
 	    unmetafy(args[n], &len[n]);
 	else
-	    args[n] = getkeystring(args[n], &len[n],
-				    func != BIN_ECHO && !ops['e'], &nnl);
+	    args[n] = getkeystring(args[n], &len[n], ops['b'] ? 2 :
+				    (func != BIN_ECHO && !ops['e']), &nnl);
 	/* -P option -- interpret as a prompt sequence */
-	if(ops['P'])
-	    args[n] = unmetafy(promptexpand(metafy(args[n], len[n],
-		META_NOALLOC), 0, NULL, NULL), &len[n]);
+	if(ops['P']) {
+	    /*
+	     * promptexpand uses permanent storage: to avoid
+	     * messy memory management, stick it on the heap
+	     * instead.
+	     */
+	    char *str = unmetafy(promptexpand(metafy(args[n], len[n],
+				   META_NOALLOC), 0, NULL, NULL), &len[n]);
+	    args[n] = dupstring(str);
+	    free(str);
+	}
 	/* -D option -- interpret as a directory, and use ~ */
 	if(ops['D']) {
 	    Nameddir d = finddir(args[n]);
 	    if(d) {
-		char *arg = alloc(strlen(args[n]) + 1);
+		char *arg = zhalloc(strlen(args[n]) + 1);
 		sprintf(arg, "~%s%s", d->nam,
 			args[n] + strlen(d->dir));
 		args[n] = arg;
@@ -2336,9 +2775,7 @@ bin_print(char *name, char **args, char *ops, int func)
 
     /* -z option -- push the arguments onto the editing buffer stack */
     if (ops['z']) {
-	PERMALLOC {
-	    pushnode(bufstack, sepjoin(args, NULL));
-	} LASTALLOC;
+	zpushnode(bufstack, sepjoin(args, NULL, 0));
 	return 0;
     }
     /* -s option -- add the arguments to the history list */
@@ -2346,28 +2783,24 @@ bin_print(char *name, char **args, char *ops, int func)
 	int nwords = 0, nlen, iwords;
 	char **pargs = args;
 
-	PERMALLOC {
-	    ent = gethistent(++curhist);
-	    zsfree(ent->text);
-	    if (ent->nwords)
-		zfree(ent->words, ent->nwords*2*sizeof(short));
-	    while (*pargs++)
-		nwords++;
-	    if ((ent->nwords = nwords)) {
-		ent->words = (short *)zalloc(nwords*2*sizeof(short));
-		nlen = iwords = 0;
-		for (pargs = args; *pargs; pargs++) {
-		    ent->words[iwords++] = nlen;
-		    nlen += strlen(*pargs);
-		    ent->words[iwords++] = nlen;
-		    nlen++;
-		}
-	    } else
-		ent->words = (short *)NULL;
-	    ent->text = zjoin(args, ' ');
-	    ent->stim = ent->ftim = time(NULL);
-	    ent->flags = 0;
-	} LASTALLOC;
+	ent = prepnexthistent(++curhist);
+	while (*pargs++)
+	    nwords++;
+	if ((ent->nwords = nwords)) {
+	    ent->words = (short *)zalloc(nwords*2*sizeof(short));
+	    nlen = iwords = 0;
+	    for (pargs = args; *pargs; pargs++) {
+		ent->words[iwords++] = nlen;
+		nlen += strlen(*pargs);
+		ent->words[iwords++] = nlen;
+		nlen++;
+	    }
+	} else
+	    ent->words = (short *)NULL;
+	ent->text = zjoin(args, ' ', 0);
+	ent->stim = ent->ftim = time(NULL);
+	ent->flags = 0;
+	addhistnode(histtab, ent->text, ent);
 	return 0;
     }
     /* -u and -p -- output to other than standard output */
@@ -2534,7 +2967,7 @@ bin_shift(char *name, char **argv, char *ops, int func)
  
     /* optional argument can be either numeric or an array */
     if (*argv && !getaparam(*argv))
-        num = matheval(*argv++);
+        num = mathevali(*argv++);
  
     if (num < 0) {
         zwarnnam(name, "argument to shift must be non-negative", NULL, 0);
@@ -2549,9 +2982,7 @@ bin_shift(char *name, char **argv, char *ops, int func)
 		    ret++;
 		    continue;
 		}
-                PERMALLOC {
-		    s = arrdup(s + num);
-                } LASTALLOC;
+		s = zarrdup(s + num);
                 setaparam(*argv, s);
             }
     } else {
@@ -2570,6 +3001,9 @@ bin_shift(char *name, char **argv, char *ops, int func)
     return ret;
 }
 
+/**/
+int optcind;
+
 /* getopts: automagical option handling for shell scripts */
 
 /**/
@@ -2579,7 +3013,6 @@ bin_getopts(char *name, char **argv, char *ops, int func)
     int lenstr, lenoptstr, quiet, lenoptbuf;
     char *optstr = unmetafy(*argv++, &lenoptstr), *var = *argv++;
     char **args = (*argv) ? argv : pparams;
-    static int optcind = 0;
     char *str, optbuf[2] = " ", *p, opch;
 
     /* zoptind keeps count of the current argument number.  The *
@@ -2627,14 +3060,14 @@ bin_getopts(char *name, char **argv, char *ops, int func)
     if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) {
 	p = "?";
 err:
-      zsfree(zoptarg);
+	zsfree(zoptarg);
+	setsparam(var, ztrdup(p));
 	if(quiet) {
-	    setsparam(var, ztrdup(p));
 	    zoptarg = metafy(optbuf, lenoptbuf, META_DUP);
 	} else {
 	    zerr(*p == '?' ? "bad option: -%c" :
 		"argument expected after -%c option", NULL, opch);
-          zoptarg=ztrdup("");
+	    zoptarg=ztrdup("");
 	    errflag = 0;
 	}
 	return 0;
@@ -2674,7 +3107,7 @@ bin_break(char *name, char **argv, char *ops, int func)
 
     /* handle one optional numeric argument */
     if (*argv) {
-	num = matheval(*argv++);
+	num = mathevali(*argv++);
 	nump = 1;
     }
 
@@ -2720,7 +3153,7 @@ bin_break(char *name, char **argv, char *ops, int func)
 /* we have printed a 'you have stopped (running) jobs.' message */
  
 /**/
-int stopmsg;
+mod_export int stopmsg;
  
 /* check to see if user has jobs running/stopped */
 
@@ -2754,42 +3187,45 @@ checkjobs(void)
  * because of a signal.                                    */
 
 /**/
-void
+mod_export void
 zexit(int val, int from_signal)
 {
     static int in_exit;
 
-    HEAPALLOC {
-	if (isset(MONITOR) && !stopmsg && !from_signal) {
-	    scanjobs();    /* check if jobs need printing           */
+    if (isset(MONITOR) && !stopmsg && !from_signal) {
+	scanjobs();    /* check if jobs need printing           */
+	if (isset(CHECKJOBS))
 	    checkjobs();   /* check if any jobs are running/stopped */
-	    if (stopmsg) {
-		stopmsg = 2;
-		LASTALLOC_RETURN;
-	    }
-	}
-	if (in_exit++ && from_signal)
+	if (stopmsg) {
+	    stopmsg = 2;
 	    LASTALLOC_RETURN;
-	if (isset(MONITOR))
-	    /* send SIGHUP to any jobs left running  */
-	    killrunjobs(from_signal);
-	if (isset(RCS) && interact) {
-	    if (!nohistsave)
-		savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0);
-	    if (islogin && !subsh) {
-		sourcehome(".zlogout");
+	}
+    }
+    if (in_exit++ && from_signal)
+	    return;
+
+    if (isset(MONITOR)) {
+	/* send SIGHUP to any jobs left running  */
+	killrunjobs(from_signal);
+    }
+    if (isset(RCS) && interact) {
+	if (!nohistsave)
+	    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
+	if (islogin && !subsh) {
+	    sourcehome(".zlogout");
 #ifdef GLOBAL_ZLOGOUT
+	    if (isset(RCS) && isset(GLOBALRCS))
 		source(GLOBAL_ZLOGOUT);
 #endif
-	    }
 	}
-	if (sigtrapped[SIGEXIT])
-	    dotrap(SIGEXIT);
-	if (mypid != getpid())
-	    _exit(val);
-	else
-	    exit(val);
-    } LASTALLOC;
+    }
+    if (sigtrapped[SIGEXIT])
+	dotrap(SIGEXIT);
+    runhookdef(EXITHOOK, NULL);
+    if (mypid != getpid())
+	_exit(val);
+    else
+	exit(val);
 }
 
 /* . (dot), source */
@@ -2808,11 +3244,9 @@ bin_dot(char *name, char **argv, char *ops, int func)
 	return 0;
     old = pparams;
     /* get arguments for the script */
-    if (argv[1]) {
-	PERMALLOC {
-	    pparams = arrdup(argv + 1);
-	} LASTALLOC;
-    }
+    if (argv[1])
+	pparams = zarrdup(argv + 1);
+
     enam = arg0 = ztrdup(*argv);
     if (isset(FUNCTIONARGZERO)) {
 	old0 = argzero;
@@ -2880,6 +3314,8 @@ int
 bin_emulate(char *nam, char **argv, char *ops, int func)
 {
     emulate(*argv, ops['R']);
+    if (ops['L'])
+	opts[LOCALOPTIONS] = opts[LOCALTRAPS] = 1;
     return 0;
 }
 
@@ -2889,19 +3325,14 @@ bin_emulate(char *nam, char **argv, char *ops, int func)
 int
 bin_eval(char *nam, char **argv, char *ops, int func)
 {
-    List list;
-
-    inpush(zjoin(argv, ' '), 0, NULL);
-    strinbeg();
-    stophist = 2;
-    list = parse_list();
-    strinend();
-    inpop();
-    if (!list) {
+    Eprog prog;
+
+    prog = parse_string(zjoin(argv, ' ', 1), 0);
+    if (!prog) {
 	errflag = 0;
 	return 1;
     }
-    execlist(list, 1, 0);
+    execode(prog, 1, 0);
     if (errflag) {
 	lastval = errflag;
 	errflag = 0;
@@ -2928,7 +3359,7 @@ bin_read(char *name, char **args, char *ops, int func)
     char *reply, *readpmpt;
     int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash;
     int haso = 0;	/* true if /dev/tty has been opened specially */
-    int isem = !strcmp(term, "emacs");
+    int isem = !strcmp(term, "emacs"), izle = zleactive && getkeyptr;
     char *buf, *bptr, *firstarg, *zbuforig;
     LinkList readll = newlinklist();
 
@@ -2937,12 +3368,11 @@ bin_read(char *name, char **args, char *ops, int func)
 	    nchars = 1;
 	args++;
     }
-
-    firstarg = *args;
-    if (*args && **args == '?')
-	args++;
-    /* default result parameter */
+    /* This `*args++ : *args' looks a bit weird, but it works around a bug
+     * in gcc-2.8.1 under DU 4.0. */
+    firstarg = (*args && **args == '?' ? *args++ : *args);
     reply = *args ? *args++ : ops['A'] ? "reply" : "REPLY";
+
     if (ops['A'] && *args) {
 	zwarnnam(name, "only one array argument allowed", NULL, 0);
 	return 1;
@@ -2953,33 +3383,37 @@ bin_read(char *name, char **args, char *ops, int func)
 	return compctlread(name, args, ops, reply);
 
     if ((ops['k'] && !ops['u'] && !ops['p']) || ops['q']) {
-	if (SHTTY == -1) {
-	    /* need to open /dev/tty specially */
-	    SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY);
-	    haso = 1;
-	}
-	/* We should have a SHTTY opened by now. */
-	if (SHTTY == -1) {
-	    /* Unfortunately, we didn't. */
-	    fprintf(stderr, "not interactive and can't open terminal\n");
-	    fflush(stderr);
-	    return 1;
+	if (!zleactive) {
+	    if (SHTTY == -1) {
+		/* need to open /dev/tty specially */
+		SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY);
+		haso = 1;
+	    }
+	    /* We should have a SHTTY opened by now. */
+	    if (SHTTY == -1) {
+		/* Unfortunately, we didn't. */
+		fprintf(stderr, "not interactive and can't open terminal\n");
+		fflush(stderr);
+		return 1;
+	    }
+	    if (unset(INTERACTIVE))
+		gettyinfo(&shttyinfo);
+	    /* attach to the tty */
+	    attachtty(mypgrp);
+	    if (!isem && ops['k'])
+		setcbreak();
+	    readfd = SHTTY;
 	}
-	if (unset(INTERACTIVE))
-	    gettyinfo(&shttyinfo);
-	/* attach to the tty */
-	attachtty(mypgrp);
-	if (!isem && ops['k'])
-	    setcbreak();
-	readfd = SHTTY;
     } else if (ops['u'] && !ops['p']) {
 	/* -u means take input from the specified file descriptor. *
 	 * -up means take input from the coprocess.                */
 	for (readfd = 9; readfd && !ops[readfd + '0']; --readfd);
-    } else if (ops['p'])
+	izle = 0;
+    } else if (ops['p']) {
 	readfd = coprocin;
-    else
-	readfd = 0;
+	izle = 0;
+    } else
+	readfd = izle = 0;
 
     /* handle prompt */
     if (firstarg) {
@@ -3003,18 +3437,25 @@ bin_read(char *name, char **args, char *ops, int func)
 	bptr = buf = (char *)zalloc(nchars+1);
 
 	do {
-	    /* If read returns 0, is end of file */
-	    if ((val = read(readfd, bptr, nchars)) <= 0)
-		break;
+	    if (izle) {
+		if ((val = getkeyptr(0)) < 0)
+		    break;
+		*bptr++ = (char) val;
+		nchars--;
+	    } else {
+		/* If read returns 0, is end of file */
+		if ((val = read(readfd, bptr, nchars)) <= 0)
+		    break;
 	    
-	    /* decrement number of characters read from number required */
-	    nchars -= val;
+		/* decrement number of characters read from number required */
+		nchars -= val;
 
-	    /* increment pointer past read characters */
-	    bptr += val;
+		/* increment pointer past read characters */
+		bptr += val;
+	    }
 	} while (nchars > 0);
 	
-	if (!ops['u'] && !ops['p']) {
+	if (!izle && !ops['u'] && !ops['p']) {
 	    /* dispose of result appropriately, etc. */
 	    if (isem)
 		while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
@@ -3043,14 +3484,19 @@ bin_read(char *name, char **args, char *ops, int func)
 	readbuf[1] = '\0';
 
 	/* get, and store, reply */
-	readbuf[0] = ((char)getquery(NULL, 0)) == 'y' ? 'y' : 'n';
+	if (izle) {
+	    int key = getkeyptr(0);
 
-	/* dispose of result appropriately, etc. */
-	if (haso) {
-	    close(SHTTY);
-	    SHTTY = -1;
-	}
+	    readbuf[0] = (key == 'y' ? 'y' : 'n');
+	} else {
+	    readbuf[0] = ((char)getquery(NULL, 0)) == 'y' ? 'y' : 'n';
 
+	    /* dispose of result appropriately, etc. */
+	    if (haso) {
+		close(SHTTY);
+		SHTTY = -1;
+	    }
+	}
 	if (ops['e'] || ops['E'])
 	    printf("%s\n", readbuf);
 	if (!ops['e'])
@@ -3072,7 +3518,7 @@ bin_read(char *name, char **args, char *ops, int func)
 	buf = bptr = (char *)zalloc(bsiz = 64);
 	/* get input, a character at a time */
 	while (!gotnl) {
-	    c = zread();
+	    c = zread(izle);
 	    /* \ at the end of a line indicates a continuation *
 	     * line, except in raw mode (-r option)            */
 	    if (bslash && c == '\n') {
@@ -3162,7 +3608,7 @@ bin_read(char *name, char **args, char *ops, int func)
     bslash = 0;
     if (!gotnl)
 	for (;;) {
-	    c = zread();
+	    c = zread(izle);
 	    /* \ at the end of a line introduces a continuation line, except in
 	    raw mode (-r option) */
 	    if (bslash && c == '\n') {
@@ -3171,13 +3617,14 @@ bin_read(char *name, char **args, char *ops, int func)
 	    }
 	    if (c == EOF || (c == '\n' && !zbuf))
 		break;
-	    if (!bslash && isep(c) && bptr == buf)
+	    if (!bslash && isep(c) && bptr == buf) {
 		if (iwsep(c))
 		    continue;
 		else if (!first) {
 		    first = 1;
 		    continue;
 		}
+	    }
 	    bslash = c == '\\' && !bslash && !ops['r'];
 	    if (bslash)
 		continue;
@@ -3225,12 +3672,17 @@ bin_read(char *name, char **args, char *ops, int func)
 
 /**/
 static int
-zread(void)
+zread(int izle)
 {
     char cc, retry = 0;
 
+    if (izle) {
+	int c = getkeyptr(0);
+
+	return (c < 0 ? EOF : c);
+    }
     /* use zbuf if possible */
-    if (zbuf)
+    if (zbuf) {
 	/* If zbuf points to anything, it points to the next character in the
 	buffer.  This may be a null byte to indicate EOF.  If reading from the
 	buffer, move on the buffer pointer. */
@@ -3238,6 +3690,7 @@ zread(void)
 	    return zbuf++, STOUC(*zbuf++ ^ 32);
 	else
 	    return (*zbuf) ? STOUC(*zbuf++) : EOF;
+    }
     for (;;) {
 	/* read a character from readfd */
 	switch (read(readfd, &cc, 1)) {
@@ -3305,7 +3758,8 @@ int
 bin_test(char *name, char **argv, char *ops, int func)
 {
     char **s;
-    Cond c;
+    Eprog prog;
+    struct estate state;
 
     /* if "test" was invoked as "[", it needs a matching "]" *
      * which is subsequently ignored                         */
@@ -3325,7 +3779,7 @@ bin_test(char *name, char **argv, char *ops, int func)
     tok = NULLTOK;
     condlex = testlex;
     testlex();
-    c = par_cond();
+    prog = parse_cond();
     condlex = yylex;
 
     if (errflag) {
@@ -3333,13 +3787,19 @@ bin_test(char *name, char **argv, char *ops, int func)
 	return 1;
     }
 
-    if (!c || tok == LEXERR) {
+    if (!prog || tok == LEXERR) {
 	zwarnnam(name, tokstr ? "parse error" : "argument expected", NULL, 0);
 	return 1;
     }
 
     /* syntax is OK, so evaluate */
-    return !evalcond(c);
+
+    state.prog = prog;
+    state.pc = prog->prog;
+    state.strs = prog->strs;
+
+
+    return !evalcond(&state);
 }
 
 /* display a time, provided in units of 1/60s, as minutes and seconds */
@@ -3374,7 +3834,7 @@ bin_times(char *name, char **argv, char *ops, int func)
 int
 bin_trap(char *name, char **argv, char *ops, int func)
 {
-    List l;
+    Eprog prog;
     char *arg, *s;
     int sig;
 
@@ -3396,7 +3856,7 @@ bin_trap(char *name, char **argv, char *ops, int func)
 		if (!sigfuncs[sig])
 		    printf("trap -- '' %s\n", sigs[sig]);
 		else {
-		    s = getpermtext((void *) dupstruct((void *) sigfuncs[sig]));
+		    s = getpermtext(sigfuncs[sig], NULL);
 		    printf("trap -- ");
 		    quotedzputs(s, stdout);
 		    printf(" %s\n", sigs[sig]);
@@ -3422,26 +3882,24 @@ bin_trap(char *name, char **argv, char *ops, int func)
     /* Sort out the command to execute on trap */
     arg = *argv++;
     if (!*arg)
-	l = NULL;
-    else if (!(l = parse_string(arg))) {
+	prog = &dummy_eprog;
+    else if (!(prog = parse_string(arg, 0))) {
 	zwarnnam(name, "couldn't parse trap command", NULL, 0);
 	return 1;
     }
 
     /* set traps */
     for (; *argv; argv++) {
-	List t;
+	Eprog t;
 
 	sig = getsignum(*argv);
 	if (sig == -1) {
 	    zwarnnam(name, "undefined signal: %s", *argv, 0);
 	    break;
 	}
-	PERMALLOC {
-	    t = (List) dupstruct(l);
-	} LASTALLOC;
+	t = dupeprog(prog, 0);
 	if (settrap(sig, t))
-	    freestruct(t);
+	    freeeprog(t);
     }
     return *argv != NULL;
 }
@@ -3465,10 +3923,10 @@ bin_ttyctl(char *name, char **argv, char *ops, int func)
 int
 bin_let(char *name, char **argv, char *ops, int func)
 {
-    long val = 0;
+    zlong val = 0;
 
     while (*argv)
-	val = matheval(*argv++);
+	val = mathevali(*argv++);
     /* Errors in math evaluation in let are non-fatal. */
     errflag = 0;
     return !val;
@@ -3591,7 +4049,7 @@ bin_umask(char *nam, char **args, char *ops, int func)
 /* Generic builtin for facilities not available on this OS */
 
 /**/
-int
+mod_export int
 bin_notavail(char *nam, char **argv, char *ops, int func)
 {
     zwarnnam(nam, "not available on this system", NULL, 0);