about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-08-02 16:49:24 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-08-02 16:49:24 +0000
commit84f5a3117ef327a8729823ff40a8a776a1724440 (patch)
treef7066dd8bb14394867100fdd5c842a8cbc999770
parent4908bb05dfceca8deb1113f056bca4d105977d1c (diff)
downloadzsh-84f5a3117ef327a8729823ff40a8a776a1724440.tar.gz
zsh-84f5a3117ef327a8729823ff40a8a776a1724440.tar.xz
zsh-84f5a3117ef327a8729823ff40a8a776a1724440.zip
zsh-workers:7336
-rw-r--r--Src/utils.c818
1 files changed, 331 insertions, 487 deletions
diff --git a/Src/utils.c b/Src/utils.c
index 86679e90f..b982b4767 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -30,23 +30,12 @@
 #include "zsh.mdh"
 #include "utils.pro"
 
-/* Print an error */
-
-/**/
-void
-zwarnnam(const char *cmd, const char *fmt, const char *str, int num)
-{
-    int waserr;
-
-    waserr = errflag;
-    zerrnam(cmd, fmt, str, num);
-    errflag = waserr;
-}
-
 /* name of script being sourced */
 
 /**/
 char *scriptname;
+
+/* Print an error */
  
 /**/
 void
@@ -57,35 +46,60 @@ zerr(const char *fmt, const char *str, int num)
 	    errflag = 1;
 	return;
     }
+    zwarn(fmt, str, num);
     errflag = 1;
+}
+
+/**/
+void
+zerrnam(const char *cmd, const char *fmt, const char *str, int num)
+{
+    if (errflag || noerrs)
+	return;
+
+    zwarnnam(cmd, fmt, str, num);
+    errflag = 1;
+}
+
+/**/
+void
+zwarn(const char *fmt, const char *str, int num)
+{
+    if (errflag || noerrs)
+	return;
     trashzle();
     /*
      * scriptname is set when sourcing scripts, so that we get the
      * correct name instead of the generic name of whatever
-     * program/script is running.
+     * program/script is running.  It's also set in shell functions,
+     * so test locallevel, too.
      */
-    nicezputs(isset(SHINSTDIN) ? "zsh" :
+    nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" :
 	      scriptname ? scriptname : argzero, stderr);
     fputs(": ", stderr);
-    zerrnam(NULL, fmt, str, num);
+    zerrmsg(fmt, str, num);
 }
 
 /**/
 void
-zerrnam(const char *cmd, const char *fmt, const char *str, int num)
+zwarnnam(const char *cmd, const char *fmt, const char *str, int num)
 {
-    if (cmd) {
-	if (errflag || noerrs)
-	    return;
-	errflag = 1;
-	trashzle();
-	if(unset(SHINSTDIN)) {
-	    nicezputs(scriptname ? scriptname : argzero, stderr);
-	    fputs(": ", stderr);
-	}
-	nicezputs(cmd, stderr);
+    if (errflag || noerrs)
+	return;
+    trashzle();
+    if (unset(SHINSTDIN) || locallevel) {
+	nicezputs(scriptname ? scriptname : argzero, stderr);
 	fputs(": ", stderr);
     }
+    nicezputs(cmd, stderr);
+    fputs(": ", stderr);
+    zerrmsg(fmt, str, num);
+}
+
+/**/
+void
+zerrmsg(const char *fmt, const char *str, int num)
+{
     while (*fmt)
 	if (*fmt == '%') {
 	    fmt++;
@@ -133,8 +147,8 @@ zerrnam(const char *cmd, const char *fmt, const char *str, int num)
 	    putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, stderr);
 	    fmt++;
 	}
-    if (unset(SHINSTDIN) && lineno)
-	fprintf(stderr, " [%ld]\n", lineno);
+    if ((unset(SHINSTDIN) || locallevel) && lineno)
+	fprintf(stderr, " [%ld]\n", (long)lineno);
     else
 	putc('\n', stderr);
     fflush(stderr);
@@ -209,10 +223,9 @@ nicechar(int c)
     return buf;
 }
 
-#if 0
 /* Output a string's visible representation. */
 
-/**/
+#if 0 /**/
 void
 nicefputs(char *s, FILE *f)
 {
@@ -302,7 +315,7 @@ slashsplit(char *s)
 
 /**/
 static int
-xsymlinks(char *s, int flag)
+xsymlinks(char *s)
 {
     char **pp, **opp;
     char xbuf2[PATH_MAX*2], xbuf3[PATH_MAX*2];
@@ -325,15 +338,9 @@ xsymlinks(char *s, int flag)
 	    *p = '\0';
 	    continue;
 	}
-	if (unset(CHASELINKS)) {
-	    strcat(xbuf, "/");
-	    strcat(xbuf, *pp);
-	    zsfree(*pp);
-	    continue;
-	}
 	sprintf(xbuf2, "%s/%s", xbuf, *pp);
 	t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
-	if (t0 == -1 || !flag) {
+	if (t0 == -1) {
 	    strcat(xbuf, "/");
 	    strcat(xbuf, *pp);
 	    zsfree(*pp);
@@ -342,9 +349,9 @@ xsymlinks(char *s, int flag)
 	    metafy(xbuf3, t0, META_NOALLOC);
 	    if (*xbuf3 == '/') {
 		strcpy(xbuf, "");
-		xsymlinks(xbuf3 + 1, flag);
+		xsymlinks(xbuf3 + 1);
 	    } else
-		xsymlinks(xbuf3, flag);
+		xsymlinks(xbuf3);
 	    zsfree(*pp);
 	}
     }
@@ -352,19 +359,19 @@ xsymlinks(char *s, int flag)
     return ret;
 }
 
-/* expand symlinks in s, and remove other weird things */
+/*
+ * expand symlinks in s, and remove other weird things:
+ * note that this always expands symlinks.
+ */
 
 /**/
 char *
 xsymlink(char *s)
 {
-    if (unset(CHASELINKS))
-	return ztrdup(s);
     if (*s != '/')
 	return NULL;
     *xbuf = '\0';
-    if (!xsymlinks(s + 1, 1))
-	return ztrdup(s);
+    xsymlinks(s + 1);
     if (!*xbuf)
 	return ztrdup("/");
     return ztrdup(xbuf);
@@ -374,15 +381,10 @@ xsymlink(char *s)
 void
 print_if_link(char *s)
 {
-    int chase;
-
     if (*s == '/') {
-	chase = opts[CHASELINKS];
-	opts[CHASELINKS] = 1;
 	*xbuf = '\0';
-	if (xsymlinks(s + 1, 1))
+	if (xsymlinks(s + 1))
 	    printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout);
-	opts[CHASELINKS] = chase;
     }
 }
 
@@ -573,7 +575,8 @@ getnameddir(char *name)
 	/* Retrieve an entry from the password table/database for this user. */
 	struct passwd *pw;
 	if ((pw = getpwnam(name))) {
-	    char *dir = xsymlink(pw->pw_dir);
+	    char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir)
+		: ztrdup(pw->pw_dir);
 	    adduserdir(name, dir, ND_USERNAME, 1);
 	    str = dupstring(dir);
 	    zsfree(dir);
@@ -775,6 +778,23 @@ checkmailpath(char **s)
     }
 }
 
+/* This prints the XTRACE prompt. */
+
+/**/
+void
+printprompt4(void)
+{
+    if (prompt4) {
+	int l;
+	char *s = dupstring(prompt4);
+
+	unmetafy(s, &l);
+	s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), 0, NULL, NULL), &l);
+
+	fprintf(stderr, "%s", s);
+    }
+}
+
 /**/
 void
 freestr(void *a)
@@ -853,27 +873,134 @@ int resetneeded;
 /**/
 int winchanged;
 #endif
- 
-/* check the size of the window and adjust if necessary */
+
+static int
+adjustlines(int signalled)
+{
+    int oldlines = lines;
+
+#ifdef TIOCGWINSZ
+    if (signalled || lines <= 0)
+	lines = shttyinfo.winsize.ws_row;
+    else
+	shttyinfo.winsize.ws_row = lines;
+#endif /* TIOCGWINSZ */
+    if (lines <= 0) {
+	DPUTS(signalled, "BUG: Impossible TIOCGWINSZ rows");
+	lines = tclines > 0 ? tclines : 24;
+    }
+
+    if (lines > 2)
+	termflags &= ~TERM_SHORT;
+    else
+	termflags |= TERM_SHORT;
+
+    return (lines != oldlines);
+}
+
+static int
+adjustcolumns(int signalled)
+{
+    int oldcolumns = columns;
+
+#ifdef TIOCGWINSZ
+    if (signalled || columns <= 0)
+	columns = shttyinfo.winsize.ws_col;
+    else
+	shttyinfo.winsize.ws_col = columns;
+#endif /* TIOCGWINSZ */
+    if (columns <= 0) {
+	DPUTS(signalled, "BUG: Impossible TIOCGWINSZ cols");
+	columns = tccolumns > 0 ? tccolumns : 80;
+    }
+
+    if (columns > 2)
+	termflags &= ~TERM_NARROW;
+    else
+	termflags |= TERM_NARROW;
+
+    return (columns != oldcolumns);
+}
+
+/* check the size of the window and adjust if necessary. *
+ * The value of from:					 *
+ *   0: called from update_job or setupvals		 *
+ *   1: called from the SIGWINCH handler		 *
+ *   2: called from the LINES parameter callback	 *
+ *   3: called from the COLUMNS parameter callback	 */
 
 /**/
 void
-adjustwinsize(void)
+adjustwinsize(int from)
 {
+    static int getwinsz = 1;
+    int ttyrows = shttyinfo.winsize.ws_row;
+    int ttycols = shttyinfo.winsize.ws_col;
+    int resetzle = 0;
+
+    if (getwinsz || from == 1) {
 #ifdef TIOCGWINSZ
-    int oldcols = columns, oldrows = lines;
+	if (SHTTY == -1)
+	    return;
+	if (ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize) == 0) {
+	    resetzle = (ttyrows != shttyinfo.winsize.ws_row ||
+			ttycols != shttyinfo.winsize.ws_col);
+	    if (from == 0 && resetzle && ttyrows && ttycols)
+		from = 1; /* Signal missed while a job owned the tty? */
+	    ttyrows = shttyinfo.winsize.ws_row;
+	    ttycols = shttyinfo.winsize.ws_col;
+	} else {
+	    /* Set to unknown on failure */
+	    shttyinfo.winsize.ws_row = 0;
+	    shttyinfo.winsize.ws_col = 0;
+	    resetzle = 1;
+	}
+#else
+	resetzle = from == 1;
+#endif /* TIOCGWINSZ */
+    } /* else
+	 return; */
+
+    switch (from) {
+    case 0:
+    case 1:
+	getwinsz = 0;
+	/* Calling setiparam() here calls this function recursively, but  *
+	 * because we've already called adjustlines() and adjustcolumns() *
+	 * here, recursive calls are no-ops unless a signal intervenes.   *
+	 * The commented "else return;" above might be a safe shortcut,   *
+	 * but I'm concerned about what happens on race conditions; e.g., *
+	 * suppose the user resizes his xterm during `eval $(resize)'?    */
+	if (adjustlines(from) && zgetenv("LINES"))
+	    setiparam("LINES", lines);
+	if (adjustcolumns(from) && zgetenv("COLUMNS"))
+	    setiparam("COLUMNS", columns);
+	getwinsz = 1;
+	break;
+    case 2:
+	resetzle = adjustlines(0);
+	break;
+    case 3:
+	resetzle = adjustcolumns(0);
+	break;
+    }
 
-    if (SHTTY == -1)
-	return;
+#ifdef TIOCGWINSZ
+    if (interact && from >= 2 &&
+	(shttyinfo.winsize.ws_row != ttyrows ||
+	 shttyinfo.winsize.ws_col != ttycols)) {
+	/* shttyinfo.winsize is already set up correctly */
+	ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize);
+    }
+#endif /* TIOCGWINSZ */
 
-    ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize);
-    setiparam("COLUMNS", shttyinfo.winsize.ws_col);
-    setiparam("LINES", shttyinfo.winsize.ws_row);
-    if (zleactive && (oldcols != columns || oldrows != lines)) {
-	resetneeded = winchanged = 1;
+    if (zleactive && resetzle) {
+#ifdef TIOCGWINSZ
+	winchanged =
+#endif /* TIOCGWINSZ */
+	    resetneeded = 1;
 	zrefresh();
     }
-#endif   /* TIOCGWINSZ */
 }
 
 /* Move a fd to a place >= 10 and mark the new fd in fdtable.  If the fd *
@@ -1060,15 +1187,15 @@ skipparens(char inpar, char outpar, char **s)
    return level;
 }
 
-/* Convert string to long.  This function (without the z) *
- * is contained in the ANSI standard C library, but a lot *
- * of them seem to be broken.                             */
+/* Convert string to zlong (see zsh.h).  This function (without the z) *
+ * is contained in the ANSI standard C library, but a lot of them seem *
+ * to be broken.                                                       */
 
 /**/
-long
+zlong
 zstrtol(const char *s, char **t, int base)
 {
-    long ret = 0;
+    zlong ret = 0;
     int neg;
 
     while (inblank(*s))
@@ -1122,7 +1249,7 @@ setblock_stdin(void)
     long mode;
 
     if (!fstat(0, &st) && !S_ISREG(st.st_mode)) {
-	mode = fcntl(0, F_GETFL);
+	mode = fcntl(0, F_GETFL, 0);
 	if (mode != -1 && (mode & NONBLOCK) &&
 	    !fcntl(0, F_SETFL, mode & ~NONBLOCK))
 	    return 1;
@@ -1611,10 +1738,11 @@ findsep(char **s, char *sep)
 	if (!*t)
 	    return i;
 	if (*(*s)++ == Meta) {
-	    (*s)++;
 #ifdef DEBUG
-	    if (! **s)
+	    if (! *(*s)++)
 		fprintf(stderr, "BUG: unexpected end of string in findsep()\n");
+#else
+	    (*s)++;
 #endif
 	}
     }
@@ -1697,13 +1825,12 @@ char *
 sepjoin(char **s, char *sep)
 {
     char *r, *p, **t;
-    int l, sl, elide = 0;
+    int l, sl;
     char sepbuf[3];
 
     if (!*s)
 	return "";
     if (!sep) {
-	elide = 1;
 	sep = sepbuf;
 	sepbuf[0] = *ifs;
 	sepbuf[1] = *ifs == Meta ? ifs[1] ^ 32 : '\0';
@@ -1817,329 +1944,21 @@ allocnode(int type)
 {
     struct node *n;
 
-    n = (struct node *) alloc(sizetab[type]);
+    n = (struct node *) ncalloc(sizetab[type]);
     memset((void *) n, 0, sizetab[type]);
     n->ntype = flagtab[type];
-    if (useheap)
-	n->ntype |= NT_HEAP;
 
     return (void *) n;
 }
+ 
+/* duplicate a syntax tree */
 
 /**/
 void *
 dupstruct(void *a)
 {
-    struct node *n, *r;
-
-    n = (struct node *) a;
-    if (!a || ((List) a) == &dummy_list)
-	return (void *) a;
-
-    if ((n->ntype & NT_HEAP) && !useheap) {
-	HEAPALLOC {
-	    n = (struct node *) dupstruct2((void *) n);
-	} LASTALLOC;
-	n = simplifystruct(n);
-    }
-    r = (struct node *)dupstruct2((void *) n);
-
-    if (!(n->ntype & NT_HEAP) && useheap)
-	r = expandstruct(r, N_LIST);
-
-    return (void *) r;
-}
-
-/**/
-static struct node *
-simplifystruct(struct node *n)
-{
-    if (!n || ((List) n) == &dummy_list)
-	return n;
-
-    switch (NT_TYPE(n->ntype)) {
-    case N_LIST:
-	{
-	    List l = (List) n;
-
-	    l->left = (Sublist) simplifystruct((struct node *)l->left);
-	    if ((l->type & Z_SYNC) && !l->right)
-		return (struct node *)l->left;
-	}
-	break;
-    case N_SUBLIST:
-	{
-	    Sublist sl = (Sublist) n;
-
-	    sl->left = (Pline) simplifystruct((struct node *)sl->left);
-	    if (sl->type == END && !sl->flags && !sl->right)
-		return (struct node *)sl->left;
-	}
-	break;
-    case N_PLINE:
-	{
-	    Pline pl = (Pline) n;
-
-	    pl->left = (Cmd) simplifystruct((struct node *)pl->left);
-	    if (pl->type == END && !pl->right)
-		return (struct node *)pl->left;
-	}
-	break;
-    case N_CMD:
-	{
-	    Cmd c = (Cmd) n;
-	    int i = 0;
-
-	    if (empty(c->args))
-		c->args = NULL, i++;
-	    if (empty(c->redir))
-		c->redir = NULL, i++;
-	    if (empty(c->vars))
-		c->vars = NULL, i++;
-
-	    c->u.list = (List) simplifystruct((struct node *)c->u.list);
-	    if (i == 3 && !c->flags &&
-		(c->type == CWHILE || c->type == CIF ||
-		 c->type == COND))
-		return (struct node *)c->u.list;
-	}
-	break;
-    case N_FOR:
-	{
-	    Forcmd f = (Forcmd) n;
-
-	    f->list = (List) simplifystruct((struct node *)f->list);
-	}
-	break;
-    case N_CASE:
-	{
-	    struct casecmd *c = (struct casecmd *)n;
-	    List *l;
-
-	    for (l = c->lists; *l; l++)
-		*l = (List) simplifystruct((struct node *)*l);
-	}
-	break;
-    case N_IF:
-	{
-	    struct ifcmd *i = (struct ifcmd *)n;
-	    List *l;
-
-	    for (l = i->ifls; *l; l++)
-		*l = (List) simplifystruct((struct node *)*l);
-	    for (l = i->thenls; *l; l++)
-		*l = (List) simplifystruct((struct node *)*l);
-	}
-	break;
-    case N_WHILE:
-	{
-	    struct whilecmd *w = (struct whilecmd *)n;
-
-	    w->cont = (List) simplifystruct((struct node *)w->cont);
-	    w->loop = (List) simplifystruct((struct node *)w->loop);
-	}
-    }
-
-    return n;
-}
-
-/**/
-struct node *
-expandstruct(struct node *n, int exp)
-{
-    struct node *m;
-
-    if (!n || ((List) n) == &dummy_list)
-	return n;
-
-    if (exp != N_COUNT && exp != NT_TYPE(n->ntype)) {
-	switch (exp) {
-	case N_LIST:
-	    {
-		List l;
-
-		m = (struct node *) allocnode(N_LIST);
-		l = (List) m;
-		l->type = Z_SYNC;
-		l->left = (Sublist) expandstruct(n, N_SUBLIST);
-
-		return (struct node *)l;
-	    }
-	case N_SUBLIST:
-	    {
-		Sublist sl;
-
-		m = (struct node *) allocnode(N_SUBLIST);
-		sl = (Sublist) m;
-		sl->type = END;
-		sl->left = (Pline) expandstruct(n, N_PLINE);
-
-		return (struct node *)sl;
-	    }
-	case N_PLINE:
-	    {
-		Pline pl;
-
-		m = (struct node *) allocnode(N_PLINE);
-		pl = (Pline) m;
-		pl->type = END;
-		pl->left = (Cmd) expandstruct(n, N_CMD);
-
-		return (struct node *)pl;
-	    }
-	case N_CMD:
-	    {
-		Cmd c;
-
-		m = (struct node *) allocnode(N_CMD);
-		c = (Cmd) m;
-		switch (NT_TYPE(n->ntype)) {
-		case N_WHILE:
-		    c->type = CWHILE;
-		    break;
-		case N_IF:
-		    c->type = CIF;
-		    break;
-		case N_COND:
-		    c->type = COND;
-		}
-		c->u.list = (List) expandstruct(n, NT_TYPE(n->ntype));
-		c->args = newlinklist();
-		c->vars = newlinklist();
-		c->redir = newlinklist();
-
-		return (struct node *)c;
-	    }
-	}
-    } else
-	switch (NT_TYPE(n->ntype)) {
-	case N_LIST:
-	    {
-		List l = (List) n;
-
-		l->left = (Sublist) expandstruct((struct node *)l->left,
-						 N_SUBLIST);
-		l->right = (List) expandstruct((struct node *)l->right,
-					       N_LIST);
-	    }
-	    break;
-	case N_SUBLIST:
-	    {
-		Sublist sl = (Sublist) n;
-
-		sl->left = (Pline) expandstruct((struct node *)sl->left,
-						N_PLINE);
-		sl->right = (Sublist) expandstruct((struct node *)sl->right,
-						   N_SUBLIST);
-	    }
-	    break;
-	case N_PLINE:
-	    {
-		Pline pl = (Pline) n;
-
-		pl->left = (Cmd) expandstruct((struct node *)pl->left,
-					      N_CMD);
-		pl->right = (Pline) expandstruct((struct node *)pl->right,
-						 N_PLINE);
-	    }
-	    break;
-	case N_CMD:
-	    {
-		Cmd c = (Cmd) n;
-
-		if (!c->args)
-		    c->args = newlinklist();
-		if (!c->vars)
-		    c->vars = newlinklist();
-		if (!c->redir)
-		    c->redir = newlinklist();
-
-		switch (c->type) {
-		case CFOR:
-		case CSELECT:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_FOR);
-		    break;
-		case CWHILE:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_WHILE);
-		    break;
-		case CIF:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_IF);
-		    break;
-		case CCASE:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_CASE);
-		    break;
-		case COND:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_COND);
-		    break;
-		case ZCTIME:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_SUBLIST);
-		    break;
-		case AUTOFN:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_AUTOFN);
-		    break;
-		default:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_LIST);
-		}
-	    }
-	    break;
-	case N_FOR:
-	    {
-		Forcmd f = (Forcmd) n;
-
-		f->list = (List) expandstruct((struct node *)f->list,
-					      N_LIST);
-	    }
-	    break;
-	case N_CASE:
-	    {
-		struct casecmd *c = (struct casecmd *)n;
-		List *l;
-
-		for (l = c->lists; *l; l++)
-		    *l = (List) expandstruct((struct node *)*l, N_LIST);
-	    }
-	    break;
-	case N_IF:
-	    {
-		struct ifcmd *i = (struct ifcmd *)n;
-		List *l;
-
-		for (l = i->ifls; *l; l++)
-		    *l = (List) expandstruct((struct node *)*l, N_LIST);
-		for (l = i->thenls; *l; l++)
-		    *l = (List) expandstruct((struct node *)*l, N_LIST);
-	    }
-	    break;
-	case N_WHILE:
-	    {
-		struct whilecmd *w = (struct whilecmd *)n;
-
-		w->cont = (List) expandstruct((struct node *)w->cont,
-					      N_LIST);
-		w->loop = (List) expandstruct((struct node *)w->loop,
-					      N_LIST);
-	    }
-	}
-
-    return n;
-}
-
-/* duplicate a syntax tree */
-
-/**/
-static void *
-dupstruct2(void *a)
-{
     void **onodes, **nnodes, *ret, *n, *on;
-    int type, heap;
+    int type;
     size_t nodeoffs;
 
     if (!a || ((List) a) == &dummy_list)
@@ -2147,53 +1966,33 @@ dupstruct2(void *a)
     type = *(int *)a;
     ret = alloc(sizetab[NT_TYPE(type)]);
     memcpy(ret, a, nodeoffs = offstab[NT_TYPE(type)]);
-    *(int*)ret = (type & ~NT_HEAP) | (useheap ? NT_HEAP : 0);
     onodes = (void **) ((char *)a + nodeoffs);
     nnodes = (void **) ((char *)ret + nodeoffs);
-    heap = type & NT_HEAP;
     for (type = (type & 0xffff00) >> 4; (type >>= 4); *nnodes++ = n) {
 	if (!(on = *onodes++))
 	    n = NULL;
 	else {
 	    switch (type & 0xf) {
 	    case NT_NODE:
-		n = dupstruct2(on);
+		n = dupstruct(on);
 		break;
 	    case NT_STR:
 		n = dupstring(on);
 		break;
 	    case NT_LIST | NT_NODE:
-		if (heap) {
-		    if (useheap)
-			n =  duplist(on, (VFunc) dupstruct2);
-		    else
-			n = list2arr(on, (VFunc) dupstruct2);
-		}
-		else if (useheap)
-		    n = arr2list(on, (VFunc) dupstruct2);
-		else
-		    n = duparray(on, (VFunc) dupstruct2);
+		n = duplist(on, (VFunc) dupstruct);
 		break;
 	    case NT_LIST | NT_STR:
-		if (heap) {
-		    if (useheap)
-			n = duplist(on, (VFunc) dupstring);
-		    else
-			n = list2arr(on, (VFunc) ztrdup);
-		}
-		else if (useheap)
-		    n = arr2list(on, (VFunc) dupstring);
-		else
-		    n = duparray(on, (VFunc) ztrdup);
+		n = duplist(on, (VFunc) (useheap ? dupstring : ztrdup));
 		break;
 	    case NT_NODE | NT_ARR:
-		n = duparray(on, (VFunc) dupstruct2);
+		n = duparray(on, (VFunc) dupstruct);
 		break;
 	    case NT_STR | NT_ARR:
 		n = duparray(on, (VFunc) (useheap ? dupstring : ztrdup));
 		break;
 	    default:
-		DPUTS(1, "BUG: bad node type in dupstruct2()");
+		DPUTS(1, "BUG: bad node type in dupstruct()");
 		abort();
 	    }
 	}
@@ -2201,19 +2000,46 @@ dupstruct2(void *a)
     return ret;
 }
 
-/* free a syntax tree */
+/* Free a syntax tree. Now, freestruct() only registers everything that
+ * has to be freed. Later, freestructs() will be called to do the real
+ * work. This is to avoid having functions that are currently executed
+ * free themselves by re-defining themselves. */
+
+static LinkList freeslist = NULL;
 
 /**/
 void
 freestruct(void *a)
 {
-    void **nodes, *n;
-    int type, size;
-
     if (!a || ((List) a) == &dummy_list)
 	return;
 
-    type = * (int *) a;
+    PERMALLOC {
+	if (!freeslist)
+	    freeslist = newlinklist();
+	addlinknode(freeslist, a);
+    } LASTALLOC;
+}
+
+/**/
+void
+freestructs(void)
+{
+    void *a;
+
+    if (freeslist)
+	while ((a = getlinknode(freeslist)))
+	    ifreestruct(a);
+}
+
+/**/
+static void
+ifreestruct(void *a)
+{
+    void **nodes, *n;
+    int type, size;
+
+    type = *(int *) a;
     nodes = (void **) ((char *)a + offstab[NT_TYPE(type)]);
     size = sizetab[NT_TYPE(type)];
     for (type = (type & 0xffff00) >> 4; (type >>= 4);) {
@@ -2226,6 +2052,8 @@ freestruct(void *a)
 		zsfree((char *) n);
 		break;
 	    case NT_LIST | NT_NODE:
+		freelinklist((LinkList) n, (FreeFunc) freestruct);
+		break;
 	    case NT_NODE | NT_ARR:
 	    {
 		void **p = (void **) n;
@@ -2236,6 +2064,8 @@ freestruct(void *a)
 		break;
 	    }
 	    case NT_LIST | NT_STR:
+		freelinklist((LinkList) n, (FreeFunc) zsfree);
+		break;
 	    case NT_STR | NT_ARR:
 		freearray((char **) n);
 		break;
@@ -2251,58 +2081,46 @@ freestruct(void *a)
 }
 
 /**/
-static LinkList
-duplist(LinkList l, VFunc func)
-{
-    LinkList ret;
-    LinkNode node;
-
-    ret = newlinklist();
-    for (node = firstnode(l); node; incnode(node))
-	addlinknode(ret, func(getdata(node)));
-    return ret;
-}
-
-/**/
-char **
-duparray(char **arr, VFunc func)
+LinkList
+dupheaplist(LinkList l)
 {
-    char **ret, **rr;
-
-    ret = (char **) alloc((arrlen(arr) + 1) * sizeof(char *));
-    for (rr = ret; *arr;)
-	*rr++ = (char *)func(*arr++);
-    *rr = NULL;
+    if (!l)
+	return NULL;
 
-    return ret;
+    return duplist(l, (VFunc) dupstruct);
 }
 
 /**/
-static char **
-list2arr(LinkList l, VFunc func)
+static LinkList
+duplist(LinkList l, VFunc func)
 {
-    char **arr, **r;
-    LinkNode n;
-
-    arr = r = (char **) alloc((countlinknodes(l) + 1) * sizeof(char *));
+    if (l && nonempty(l)) {
+	LinkList ret;
+	LinkNode node;
 
-    for (n = firstnode(l); n; incnode(n))
-	*r++ = (char *)func(getdata(n));
-    *r = NULL;
-
-    return arr;
+	ret = newlinklist();
+	for (node = firstnode(l); node; incnode(node))
+	    addlinknode(ret, func(getdata(node)));
+	return ret;
+    }
+    return NULL;
 }
 
 /**/
-static LinkList
-arr2list(char **arr, VFunc func)
+char **
+duparray(char **arr, VFunc func)
 {
-    LinkList l = newlinklist();
+    if (arr && *arr) {
+	char **ret, **rr, *p;
 
-    while (*arr)
-	addlinknode(l, func(*arr++));
+	ret = (char **) alloc((arrlen(arr) + 1) * sizeof(char *));
+	for (rr = ret; (p = *arr++);)
+	    *rr++ = (char *)func(p);
+	*rr = NULL;
 
-    return l;
+	return ret;
+    }
+    return NULL;
 }
 
 /**/
@@ -2320,7 +2138,12 @@ mkarray(char *s)
 void
 zbeep(void)
 {
-    if (isset(BEEP))
+    char *vb;
+    if ((vb = getsparam("ZBEEP"))) {
+	int len;
+	vb = getkeystring(vb, &len, 2, NULL);
+	write(SHTTY, vb, len);
+    } else if (isset(BEEP))
 	write(SHTTY, "\07", 1);
 }
 
@@ -2348,28 +2171,6 @@ equalsplit(char *s, char **t)
     return 0;
 }
 
-/* see if the right side of a list is trivial */
-
-/**/
-void
-simplifyright(List l)
-{
-    Cmd c;
-
-    if (l == &dummy_list || !l->right)
-	return;
-    if (l->right->right || l->right->left->right ||
-	l->right->left->flags || l->right->left->left->right ||
-	l->left->flags)
-	return;
-    c = l->left->left->left;
-    if (c->type != SIMPLE || nonempty(c->args) || nonempty(c->redir)
-	|| nonempty(c->vars))
-	return;
-    l->right = NULL;
-    return;
-}
-
 /* the ztypes table */
 
 /**/
@@ -2434,6 +2235,25 @@ arrdup(char **s)
     return y;
 }
 
+/* Duplicate a list of strings. */
+
+/**/
+LinkList
+listdup(LinkList l)
+{
+    if (!l)
+	return NULL;
+    else {
+	LinkList r = newlinklist();
+	LinkNode n;
+
+	for (n = firstnode(l); n; incnode(n))
+	    addlinknode(r, dupstring((char *) getdata(n)));
+
+	return r;
+    }
+}
+
 /**/
 char **
 listarr(LinkList l)
@@ -3343,10 +3163,9 @@ dquotedztrdup(char const *s)
     return ret;
 }
 
-#if 0
 /* Unmetafy and output a string, double quoting it in its entirety. */
 
-/**/
+#if 0 /**/
 int
 dquotedzputs(char const *s, FILE *stream)
 {
@@ -3358,22 +3177,42 @@ dquotedzputs(char const *s, FILE *stream)
 }
 #endif
 
+/*
+ * Decode a key string, turning it into the literal characters.
+ * The length is returned in len.
+ * fromwhere determines how the processing works.
+ *   0:  Don't handle keystring, just print-like escapes.
+ *       Expects misc to be present.
+ *   1:  Handle Emacs-like \C-X arguments etc., but not ^X
+ *       Expects misc to be present.
+ *   2:  Handle ^X as well as emacs-like keys; don't handle \c
+ *       for no newlines.
+ *   3:  As 1, but don't handle \c.
+ *   4:  Do $'...' quoting.  Overwrites the existing string instead of
+ *       zhalloc'ing 
+ *   5:  As 2, but \- is special.  Expects misc to be defined.
+ *   6:  As 2, but parses only one character and returns end-pointer
+ *       and parsed character in *misc
+ */
+
 /**/
 char *
 getkeystring(char *s, int *len, int fromwhere, int *misc)
 {
-    char *buf;
+    char *buf, tmp[1];
     char *t, *u = NULL;
     char svchar = '\0';
     int meta = 0, control = 0;
 
-    if (fromwhere != 4)
-	buf = zhalloc(strlen(s) + 1);
+    if (fromwhere == 6)
+	t = buf = tmp;
+    else if (fromwhere != 4)
+	t = buf = zhalloc(strlen(s) + 1);
     else {
-	buf = s;
+	t = buf = s;
 	s += 2;
     }
-    for (t = buf; *s; s++) {
+    for (; *s; s++) {
 	if (*s == '\\' && s[1]) {
 	    switch (*++s) {
 	    case 'a':
@@ -3472,7 +3311,8 @@ getkeystring(char *s, int *len, int fromwhere, int *misc)
 	} else if (fromwhere == 4 && *s == Snull) {
 	    for (u = t; (*u++ = *s++););
 	    return t + 1;
-	} else if (*s == '^' && (fromwhere == 2 || fromwhere == 5)) {
+	} else if (*s == '^' &&
+		   (fromwhere == 2 || fromwhere == 5 || fromwhere == 6)) {
 	    control = 1;
 	    continue;
 	} else if (*s == Meta)
@@ -3499,6 +3339,10 @@ getkeystring(char *s, int *len, int fromwhere, int *misc)
 	    t[-1] = Meta;
 	    t++;
 	}
+	if (fromwhere == 6 && t != tmp) {
+	    *misc = (int) tmp[0];
+	    return s + 1;
+	}
     }
     DPUTS(fromwhere == 4, "BUG: unterminated $' substitution");
     *t = '\0';