about summary refs log tree commit diff
path: root/Src/params.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/params.c')
-rw-r--r--Src/params.c2191
1 files changed, 2191 insertions, 0 deletions
diff --git a/Src/params.c b/Src/params.c
new file mode 100644
index 000000000..4f7846820
--- /dev/null
+++ b/Src/params.c
@@ -0,0 +1,2191 @@
+/*
+ * params.c - parameters
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zsh.mdh"
+#include "params.pro"
+
+#include "version.h"
+
+/* what level of localness we are at */
+ 
+/**/
+int locallevel;
+ 
+/* Variables holding values of special parameters */
+ 
+/**/
+char **pparams,		/* $argv        */
+     **cdpath,		/* $cdpath      */
+     **fignore,		/* $fignore     */
+     **fpath,		/* $fpath       */
+     **mailpath,	/* $mailpath    */
+     **manpath,		/* $manpath     */
+     **path,		/* $path        */
+     **psvar,		/* $psvar       */
+     **watch;		/* $watch       */
+ 
+/**/
+char *argzero,		/* $0           */
+     *underscore,	/* $_           */
+     *home,		/* $HOME        */
+     *hostnam,		/* $HOST        */
+     *ifs,		/* $IFS         */
+     *nullcmd,		/* $NULLCMD     */
+     *oldpwd,		/* $OLDPWD      */
+     *zoptarg,		/* $OPTARG      */
+     *postedit,		/* $POSTEDIT    */
+     *prompt,		/* $PROMPT      */
+     *prompt2,		/* $PROMPT2     */
+     *prompt3,		/* $PROMPT3     */
+     *prompt4,		/* $PROMPT4     */
+     *pwd,		/* $PWD         */
+     *readnullcmd,	/* $READNULLCMD */
+     *rprompt,		/* $RPROMPT     */
+     *sprompt,		/* $SPROMPT     */
+     *term,		/* $TERM        */
+     *ttystrname,	/* $TTY         */
+     *wordchars,	/* $WORDCHARS   */
+     *zsh_name;		/* $ZSH_NAME    */
+
+/**/
+long lastval,		/* $?           */
+     mypid,		/* $$           */
+     lastpid,		/* $!           */
+     columns,		/* $COLUMNS     */
+     lineno,		/* $LINENO      */
+     lines,		/* $LINES       */
+     zoptind,		/* $OPTIND      */
+     ppid,		/* $PPID        */
+     shlvl;		/* $SHLVL       */
+
+/* $histchars */
+ 
+/**/
+unsigned char bangchar, hatchar, hashchar;
+ 
+/* $SECONDS = time(NULL) - shtimer.tv_sec */
+ 
+/**/
+struct timeval shtimer;
+ 
+/* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */
+
+/**/
+int termflags;
+ 
+/* Nodes for special parameters for parameter hash table */
+
+static
+#ifdef HAVE_UNION_INIT
+# define BR(X) {X}
+struct param
+#else
+# define BR(X) X
+struct iparam {
+    struct hashnode *next;
+    char *nam;			/* hash data                             */
+    int flags;			/* PM_* flags (defined in zsh.h)         */
+    void *value;
+    void (*func1) _((void));	/* set func                              */
+    char *(*func2) _((void));	/* get func                              */
+    void (*unsetfn) _((Param, int));	/* unset func                    */
+    int ct;			/* output base or field width            */
+    char *env;			/* location in environment, if exported  */
+    char *ename;		/* name of corresponding environment var */
+    Param old;			/* old struct for use with local         */
+    int level;			/* if (old != NULL), level of localness  */
+}
+#endif
+special_params[] ={
+#define SFN(X) BR(((void (*)_((Param, char *)))(X)))
+#define GFN(X) BR(((char *(*)_((Param)))(X)))
+#define IPDEF1(A,B,C,D) {NULL,A,PM_INTEGER|PM_SPECIAL|D,BR(NULL),SFN(C),GFN(B),stdunsetfn,10,NULL,NULL,NULL,0}
+IPDEF1("#", poundgetfn, nullsetfn, PM_READONLY),
+IPDEF1("ERRNO", errnogetfn, nullsetfn, PM_READONLY),
+IPDEF1("GID", gidgetfn, gidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
+IPDEF1("EGID", egidgetfn, egidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
+IPDEF1("HISTSIZE", histsizegetfn, histsizesetfn, PM_RESTRICTED),
+IPDEF1("RANDOM", randomgetfn, randomsetfn, 0),
+IPDEF1("SECONDS", secondsgetfn, secondssetfn, 0),
+IPDEF1("UID", uidgetfn, uidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
+IPDEF1("EUID", euidgetfn, euidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
+IPDEF1("TTYIDLE", ttyidlegetfn, nullsetfn, PM_READONLY),
+
+#define IPDEF2(A,B,C,D) {NULL,A,PM_SCALAR|PM_SPECIAL|D,BR(NULL),SFN(C),GFN(B),stdunsetfn,0,NULL,NULL,NULL,0}
+IPDEF2("USERNAME", usernamegetfn, usernamesetfn, PM_DONTIMPORT|PM_RESTRICTED),
+IPDEF2("-", dashgetfn, nullsetfn, PM_READONLY),
+IPDEF2("histchars", histcharsgetfn, histcharssetfn, PM_DONTIMPORT),
+IPDEF2("HOME", homegetfn, homesetfn, 0),
+IPDEF2("TERM", termgetfn, termsetfn, 0),
+IPDEF2("WORDCHARS", wordcharsgetfn, wordcharssetfn, 0),
+IPDEF2("IFS", ifsgetfn, ifssetfn, PM_DONTIMPORT),
+IPDEF2("_", underscoregetfn, nullsetfn, PM_READONLY),
+
+#ifdef LC_ALL
+# define LCIPDEF(name) IPDEF2(name, strgetfn, lcsetfn, PM_UNSET)
+IPDEF2("LANG", strgetfn, langsetfn, PM_UNSET),
+IPDEF2("LC_ALL", strgetfn, lc_allsetfn, PM_UNSET),
+# ifdef LC_COLLATE
+LCIPDEF("LC_COLLATE"),
+# endif
+# ifdef LC_CTYPE
+LCIPDEF("LC_CTYPE"),
+# endif
+# ifdef LC_MESSAGES
+LCIPDEF("LC_MESSAGES"),
+# endif
+# ifdef LC_TIME
+LCIPDEF("LC_TIME"),
+# endif
+#endif
+
+#define IPDEF4(A,B) {NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL,BR((void *)B),SFN(nullsetfn),GFN(intvargetfn),stdunsetfn,10,NULL,NULL,NULL,0}
+IPDEF4("!", &lastpid),
+IPDEF4("$", &mypid),
+IPDEF4("?", &lastval),
+IPDEF4("LINENO", &lineno),
+IPDEF4("PPID", &ppid),
+
+#define IPDEF5(A,B,F) {NULL,A,PM_INTEGER|PM_SPECIAL,BR((void *)B),SFN(F),GFN(intvargetfn),stdunsetfn,10,NULL,NULL,NULL,0}
+IPDEF5("COLUMNS", &columns, zlevarsetfn),
+IPDEF5("LINES", &lines, zlevarsetfn),
+IPDEF5("OPTIND", &zoptind, intvarsetfn),
+IPDEF5("SHLVL", &shlvl, intvarsetfn),
+
+#define IPDEF7(A,B) {NULL,A,PM_SCALAR|PM_SPECIAL,BR((void *)B),SFN(strvarsetfn),GFN(strvargetfn),stdunsetfn,0,NULL,NULL,NULL,0}
+IPDEF7("OPTARG", &zoptarg),
+IPDEF7("NULLCMD", &nullcmd),
+IPDEF7("POSTEDIT", &postedit),
+IPDEF7("READNULLCMD", &readnullcmd),
+IPDEF7("RPROMPT", &rprompt),
+IPDEF7("PS1", &prompt),
+IPDEF7("PS2", &prompt2),
+IPDEF7("PS3", &prompt3),
+IPDEF7("PS4", &prompt4),
+IPDEF7("RPS1", &rprompt),
+IPDEF7("SPROMPT", &sprompt),
+IPDEF7("0", &argzero),
+
+#define IPDEF8(A,B,C,D) {NULL,A,D|PM_SCALAR|PM_SPECIAL,BR((void *)B),SFN(colonarrsetfn),GFN(colonarrgetfn),stdunsetfn,0,NULL,C,NULL,0}
+IPDEF8("CDPATH", &cdpath, "cdpath", 0),
+IPDEF8("FIGNORE", &fignore, "fignore", 0),
+IPDEF8("FPATH", &fpath, "fpath", 0),
+IPDEF8("MAILPATH", &mailpath, "mailpath", 0),
+IPDEF8("WATCH", &watch, "watch", 0),
+IPDEF8("PATH", &path, "path", PM_RESTRICTED),
+IPDEF8("PSVAR", &psvar, "psvar", 0),
+
+#ifdef DYNAMIC
+/* MODULE_PATH is not imported for security reasons */
+IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED),
+#endif
+
+#define IPDEF9F(A,B,C,D) {NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT,BR((void *)B),SFN(arrvarsetfn),GFN(arrvargetfn),stdunsetfn,0,NULL,C,NULL,0}
+#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0)
+IPDEF9("*", &pparams, NULL),
+IPDEF9("@", &pparams, NULL),
+{NULL, NULL},
+
+/* The following parameters are not avaible in sh/ksh compatibility *
+ * mode. All of these has sh compatible equivalents.                */
+IPDEF1("ARGC", poundgetfn, nullsetfn, PM_READONLY),
+IPDEF2("HISTCHARS", histcharsgetfn, histcharssetfn, PM_DONTIMPORT),
+IPDEF4("status", &lastval),
+IPDEF7("prompt", &prompt),
+IPDEF7("PROMPT", &prompt),
+IPDEF7("PROMPT2", &prompt2),
+IPDEF7("PROMPT3", &prompt3),
+IPDEF7("PROMPT4", &prompt4),
+IPDEF8("MANPATH", &manpath, "manpath", 0),
+IPDEF9("argv", &pparams, NULL),
+IPDEF9("fignore", &fignore, "FIGNORE"),
+IPDEF9("cdpath", &cdpath, "CDPATH"),
+IPDEF9("fpath", &fpath, "FPATH"),
+IPDEF9("mailpath", &mailpath, "MAILPATH"),
+IPDEF9("manpath", &manpath, "MANPATH"),
+IPDEF9("psvar", &psvar, "PSVAR"),
+IPDEF9("watch", &watch, "WATCH"),
+
+#ifdef DYNAMIC
+IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED),
+#endif
+IPDEF9F("path", &path, "PATH", PM_RESTRICTED),
+
+{NULL, NULL}
+};
+#undef BR
+
+static Param argvparam;
+
+/* hash table containing the parameters */
+ 
+/**/
+HashTable paramtab;
+ 
+/* Set up parameter hash table.  This will add predefined  *
+ * parameter entries as well as setting up parameter table *
+ * entries for environment variables we inherit.           */
+
+/**/
+void
+createparamtable(void)
+{
+    Param ip, pm;
+    char **new_environ, **envp, **envp2, **sigptr, **t;
+    char buf[50], *str, *iname;
+    int num_env;
+
+    paramtab = newhashtable(151, "paramtab", NULL);
+
+    paramtab->hash        = hasher;
+    paramtab->emptytable  = NULL;
+    paramtab->filltable   = NULL;
+    paramtab->addnode     = addhashnode;
+    paramtab->getnode     = gethashnode2;
+    paramtab->getnode2    = gethashnode2;
+    paramtab->removenode  = removehashnode;
+    paramtab->disablenode = NULL;
+    paramtab->enablenode  = NULL;
+    paramtab->freenode    = freeparamnode;
+    paramtab->printnode   = printparamnode;
+
+    /* Add the special parameters to the hash table */
+    for (ip = special_params; ip->nam; ip++)
+	paramtab->addnode(paramtab, ztrdup(ip->nam), ip);
+    if (emulation != EMULATE_SH && emulation != EMULATE_KSH)
+	while ((++ip)->nam)
+	    paramtab->addnode(paramtab, ztrdup(ip->nam), ip);
+
+    argvparam = (Param) paramtab->getnode(paramtab, "*");
+
+    noerrs = 1;
+
+    HEAPALLOC {
+	/* Add the standard non-special parameters which have to    *
+	 * be initialized before we copy the environment variables. *
+	 * We don't want to override whatever values the users has  *
+	 * given them in the environment.                           */
+	setiparam("MAILCHECK", 60);
+	setiparam("LOGCHECK", 60);
+	setiparam("KEYTIMEOUT", 40);
+	setiparam("LISTMAX", 100);
+#ifdef HAVE_SELECT
+	setiparam("BAUD", getbaudrate(&shttyinfo));  /* get the output baudrate */
+#endif
+	setsparam("FCEDIT", ztrdup(DEFAULT_FCEDIT));
+	setsparam("TMPPREFIX", ztrdup(DEFAULT_TMPPREFIX));
+	setsparam("TIMEFMT", ztrdup(DEFAULT_TIMEFMT));
+	setsparam("WATCHFMT", ztrdup(default_watchfmt));
+	setsparam("HOST", ztrdup(hostnam));
+	setsparam("LOGNAME", ztrdup((str = getlogin()) && *str ? str : cached_username));
+
+	/* Copy the environment variables we are inheriting to dynamic *
+	 * memory, so we can do mallocs and frees on it.               */
+	num_env = arrlen(environ);
+	new_environ = (char **) zalloc(sizeof(char *) * (num_env + 1));
+	*new_environ = NULL;
+
+	/* Now incorporate environment variables we are inheriting *
+	 * into the parameter hash table.                          */
+	for (envp = new_environ, envp2 = environ; *envp2; envp2++) {
+	    for (str = *envp2; *str && *str != '='; str++);
+	    if (*str == '=') {
+		iname = NULL;
+		*str = '\0';
+		if (!idigit(**envp2) && isident(*envp2) && !strchr(*envp2, '[')) {
+		    iname = *envp2;
+		    if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) ||
+			 !(pm->flags & PM_DONTIMPORT)) &&
+			(pm = setsparam(iname, metafy(str + 1, -1, META_DUP))) &&
+			!(pm->flags & PM_EXPORTED)) {
+			*str = '=';
+			pm->flags |= PM_EXPORTED;
+			pm->env = *envp++ = ztrdup(*envp2);
+			*envp = NULL;
+			if (pm->flags & PM_SPECIAL)
+			    pm->env = replenv(pm->env, getsparam(pm->nam));
+		    }
+		}
+		*str = '=';
+	    }
+	}
+	environ = new_environ;
+
+	pm = (Param) paramtab->getnode(paramtab, "HOME");
+	if (!(pm->flags & PM_EXPORTED)) {
+	    pm->flags |= PM_EXPORTED;
+	    pm->env = addenv("HOME", home);
+	}
+	pm = (Param) paramtab->getnode(paramtab, "LOGNAME");
+	if (!(pm->flags & PM_EXPORTED)) {
+	    pm->flags |= PM_EXPORTED;
+	    pm->env = addenv("LOGNAME", pm->u.str);
+	}
+	pm = (Param) paramtab->getnode(paramtab, "SHLVL");
+	if (!(pm->flags & PM_EXPORTED))
+	    pm->flags |= PM_EXPORTED;
+	sprintf(buf, "%d", (int)++shlvl);
+	pm->env = addenv("SHLVL", buf);
+
+	/* Add the standard non-special parameters */
+	set_pwd_env();
+	setsparam("MACHTYPE", ztrdup(MACHTYPE));
+	setsparam("OSTYPE", ztrdup(OSTYPE));
+	setsparam("TTY", ztrdup(ttystrname));
+	setsparam("VENDOR", ztrdup(VENDOR));
+	setsparam("ZSH_NAME", ztrdup(zsh_name));
+	setsparam("ZSH_VERSION", ztrdup(ZSH_VERSION));
+	setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
+	for (t = sigs; (*sigptr++ = ztrdup(*t++)); );
+    } LASTALLOC;
+
+    noerrs = 0;
+}
+
+/* Create a parameter, so that it can be assigned to.  Returns NULL if the *
+ * parameter already exists or can't be created, otherwise returns the     *
+ * parameter node.  If a parameter of the same name exists in an outer     *
+ * scope, it is hidden by a newly created parameter.  An already existing  *
+ * parameter node at the current level may be `created' and returned       *
+ * provided it is unset and not special.  If the parameter can't be        *
+ * created because it already exists, the PM_UNSET flag is cleared.        */
+
+/**/
+Param
+createparam(char *name, int flags)
+{
+    Param pm, oldpm;
+
+    if (name != nulstring) {
+	oldpm = (Param) paramtab->getnode(paramtab, name);
+
+	if (oldpm && oldpm->level == locallevel) {
+	    if (!(oldpm->flags & PM_UNSET) || (oldpm->flags & PM_SPECIAL)) {
+		oldpm->flags &= ~PM_UNSET;
+		return NULL;
+	    }
+	    if ((oldpm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
+		zerr("%s: restricted", name, 0);
+		return NULL;
+	    }
+
+	    pm = oldpm;
+	    pm->ct = 0;
+	    oldpm = pm->old;
+	} else {
+	    pm = (Param) zcalloc(sizeof *pm);
+	    if ((pm->old = oldpm)) {
+		/* needed to avoid freeing oldpm */
+		paramtab->removenode(paramtab, name);
+	    }
+	    paramtab->addnode(paramtab, ztrdup(name), pm);
+	}
+
+	if (isset(ALLEXPORT) && !oldpm)
+	    flags |= PM_EXPORTED;
+    } else
+	pm = (Param) alloc(sizeof *pm);
+    pm->flags = flags;
+
+    if(!(pm->flags & PM_SPECIAL)) {
+	switch (PM_TYPE(flags)) {
+	case PM_SCALAR:
+	    pm->sets.cfn = strsetfn;
+	    pm->gets.cfn = strgetfn;
+	    break;
+	case PM_INTEGER:
+	    pm->sets.ifn = intsetfn;
+	    pm->gets.ifn = intgetfn;
+	    break;
+	case PM_ARRAY:
+	    pm->sets.afn = arrsetfn;
+	    pm->gets.afn = arrgetfn;
+	    break;
+	default:
+	    DPUTS(1, "BUG: tried to create param node without valid flag");
+	    break;
+	}
+	pm->unsetfn = stdunsetfn;
+    }
+    return pm;
+}
+
+/* Return 1 if the string s is a valid identifier, else return 0. */
+
+/**/
+int
+isident(char *s)
+{
+    char *ss;
+    int ne;
+
+    ne = noeval;		/* save the current value of noeval     */
+    if (!*s)			/* empty string is definitely not valid */
+	return 0;
+
+    /* find the first character in `s' not in the iident type table */
+    for (ss = s; *ss; ss++)
+	if (!iident(*ss))
+	    break;
+
+    /* If this exhaust `s' or the next two characters *
+     * are [(, then it is a valid identifier.         */
+    if (!*ss || (*ss == '[' && ss[1] == '('))
+	return 1;
+
+    /* Else if the next character is not [, then it is *
+     * definitely not a valid identifier.              */
+    if (*ss != '[')
+	return 0;
+    noeval = 1;
+    (void)mathevalarg(++ss, &ss);
+    if (*ss == ',')
+	(void)mathevalarg(++ss, &ss);
+    noeval = ne;		/* restore the value of noeval */
+    if (*ss != ']' || ss[1])
+	return 0;
+    return 1;
+}
+
+static char **garr;
+
+/**/
+static long
+getarg(char **str, int *inv, Value v, int a2, long *w)
+{
+    int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i;
+    char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt;
+    long r = 0;
+    Comp c;
+
+    /* first parse any subscription flags */
+    if (*s == '(' || *s == Inpar) {
+	int escapes = 0;
+	int waste;
+	for (s++; *s != ')' && *s != Outpar && s != *str; s++) {
+	    switch (*s) {
+	    case 'r':
+		rev = 1;
+		down = ind = 0;
+		break;
+	    case 'R':
+		rev = down = 1;
+		ind = 0;
+		break;
+	    case 'i':
+		rev = ind = 1;
+		down = 0;
+		break;
+	    case 'I':
+		rev = ind = down = 1;
+		break;
+	    case 'w':
+		/* If the parameter is a scalar, then make subscription *
+		 * work on a per-word basis instead of characters.      */
+		word = 1;
+		break;
+	    case 'f':
+		word = 1;
+		sep = "\n";
+		break;
+	    case 'e':
+		/* obsolate compatibility flag without any real effect */
+		break;
+	    case 'n':
+		t = get_strarg(++s);
+		if (!*t)
+		    goto flagerr;
+		sav = *t;
+		*t = '\0';
+		num = mathevalarg(s + 1, &d);
+		if (!num)
+		    num = 1;
+		*t = sav;
+		s = t;
+		break;
+	    case 'p':
+		escapes = 1;
+		break;
+	    case 's':
+		/* This gives the string that separates words *
+		 * (for use with the `w' flag.                */
+		t = get_strarg(++s);
+		if (!*t)
+		    goto flagerr;
+		sav = *t;
+		*t = '\0';
+		sep = escapes ? getkeystring(s + 1, &waste, 1, &waste) :
+				dupstring(s + 1);
+		*t = sav;
+		s = t;
+		break;
+	    default:
+	      flagerr:
+		num = 1;
+		word = rev = ind = down = 0;
+		sep = NULL;
+		s = *str - 1;
+	    }
+	}
+	if (s != *str)
+	    s++;
+    }
+    if (num < 0) {
+	down = !down;
+	num = -num;
+    }
+    *inv = ind;
+
+    for (t=s, i=0; *t && ((*t != ']' && *t != Outbrack && *t != ',') || i); t++)
+	if (*t == '[' || *t == Inbrack)
+	    i++;
+	else if (*t == ']' || *t == Outbrack)
+	    i--;
+
+    if (!*t)
+	return 0;
+    s = dupstrpfx(s, t - s);
+    *str = tt = t;
+    if (parsestr(s))
+	return 0;
+    singsub(&s);
+
+    if (!rev) {
+	if (!(r = mathevalarg(s, &s)) || (isset(KSHARRAYS) && r >= 0))
+	    r++;
+	if (word && !v->isarr) {
+	    s = t = getstrvalue(v);
+	    i = wordcount(s, sep, 0);
+	    if (r < 0)
+		r += i + 1;
+	    if (r < 1)
+		r = 1;
+	    if (r > i)
+		r = i;
+	    if (!s || !*s)
+		return 0;
+	    while ((d = findword(&s, sep)) && --r);
+	    if (!d)
+		return 0;
+
+	    if (!a2 && *tt != ',')
+		*w = (long)(s - t) - 1;
+
+	    return (a2 ? s : d + 1) - t;
+	} else if (!v->isarr && !word) {
+	    s = getstrvalue(v);
+	    if (r > 0) {
+		for (t = s + r - 1; *s && s < t;)
+		    if (*s++ == Meta)
+			s++, t++, r++;
+	    } else {
+		r += ztrlen(s);
+		for (t = s + r; *s && s < t; r--)
+		    if (*s++ == Meta)
+			t++, r++;
+		r -= strlen(s);
+	    }
+	}
+    } else {
+	if (!v->isarr && !word) {
+	    l = strlen(s);
+	    if (a2) {
+		if (!l || *s != '*') {
+		    d = (char *) ncalloc(l + 2);
+		    *d = '*';
+		    strcpy(d + 1, s);
+		    s = d;
+		}
+	    } else {
+		if (!l || s[l - 1] != '*') {
+		    d = (char *) ncalloc(l + 2);
+		    strcpy(d, s);
+		    strcat(d, "*");
+		    s = d;
+		}
+	    }
+	}
+	tokenize(s);
+
+	if ((c = parsereg(s))) {
+	    if (v->isarr) {
+		ta = getarrvalue(v);
+		if (!ta || !*ta)
+		    return 0;
+		if (down)
+		    for (r = -1, p = ta + arrlen(ta) - 1; p >= ta; r--, p--) {
+			if (domatch(*p, c, 0) && !--num)
+			    return r;
+		} else
+		    for (r = 1, p = ta; *p; r++, p++)
+			if (domatch(*p, c, 0) && !--num)
+			    return r;
+	    } else if (word) {
+		ta = sepsplit(d = s = getstrvalue(v), sep, 1);
+		if (down) {
+		    for (p = ta + (r = arrlen(ta)) - 1; p >= ta; p--, r--)
+			if (domatch(*p, c, 0) && !--num)
+			    break;
+		    if (p < ta)
+			return 0;
+		} else {
+		    for (r = 1, p = ta; *p; r++, p++)
+			if (domatch(*p, c, 0) && !--num)
+			    break;
+		    if (!*p)
+			return 0;
+		}
+		if (a2)
+		    r++;
+		for (i = 0; (t = findword(&d, sep)) && *t; i++)
+		    if (!--r) {
+			r = (long)(t - s + (a2 ? -1 : 1));
+			if (!a2 && *tt != ',')
+			    *w = r + strlen(ta[i]) - 2;
+			return r;
+		    }
+		return a2 ? -1 : 0;
+	    } else {
+		d = getstrvalue(v);
+		if (!d || !*d)
+		    return 0;
+		if (a2) {
+		    if (down)
+			for (r = -2, t = d + strlen(d) - 1; t >= d; r--, t--) {
+			    sav = *t;
+			    *t = '\0';
+			    if (domatch(d, c, 0) && !--num) {
+				*t = sav;
+				return r;
+			    }
+			    *t = sav;
+		    } else
+			for (r = 0, t = d; *t; r++, t++) {
+			    sav = *t;
+			    *t = '\0';
+			    if (domatch(d, c, 0) && !--num) {
+				*t = sav;
+				return r;
+			    }
+			    *t = sav;
+			}
+		} else {
+		    if (down)
+			for (r = -1, t = d + strlen(d) - 1; t >= d; r--, t--) {
+			    if (domatch(t, c, 0) && !--num)
+				return r;
+		    } else
+			for (r = 1, t = d; *t; r++, t++)
+			    if (domatch(t, c, 0) && !--num)
+				return r;
+		}
+		return 0;
+	    }
+	}
+    }
+    return r;
+}
+
+/**/
+int
+getindex(char **pptr, Value v)
+{
+    int a, b, inv = 0;
+    char *s = *pptr, *tbrack;
+
+    *s++ = '[';
+    for (tbrack = s; *tbrack && *tbrack != ']' && *tbrack != Outbrack; tbrack++)
+	if (itok(*tbrack))
+	    *tbrack = ztokens[*tbrack - Pound];
+    if (*tbrack == Outbrack)
+	*tbrack = ']';
+    if ((s[0] == '*' || s[0] == '@') && s[1] == ']') {
+	if (v->isarr)
+	    v->isarr = (s[0] == '*') ? 1 : -1;
+	v->a = 0;
+	v->b = -1;
+	s += 2;
+    } else {
+	long we = 0, dummy;
+
+	a = getarg(&s, &inv, v, 0, &we);
+
+	if (inv) {
+	    if (!v->isarr && a != 0) {
+		char *t, *p;
+		t = getstrvalue(v);
+		if (a > 0) {
+		    for (p = t + a - 1; p-- > t; )
+			if (*p == Meta)
+			    a--;
+		} else
+		    a = -ztrlen(t + a + strlen(t));
+	    }
+	    if (a > 0 && isset(KSHARRAYS))
+		a--;
+	    v->inv = 1;
+	    v->isarr = 0;
+	    v->a = v->b = a;
+	    if (*s == ',') {
+		zerr("invalid subscript", NULL, 0);
+		while (*s != ']' && *s != Outbrack)
+		    s++;
+		*pptr = s;
+		return 1;
+	    }
+	    if (*s == ']' || *s == Outbrack)
+		s++;
+	} else {
+	    if (a > 0)
+		a--;
+	    if (*s == ',') {
+		s++;
+		b = getarg(&s, &inv, v, 1, &dummy);
+		if (b > 0)
+		    b--;
+	    } else {
+		b = we ? we : a;
+	    }
+	    if (*s == ']' || *s == Outbrack) {
+		s++;
+		if (v->isarr && a == b)
+		    v->isarr = 0;
+		v->a = a;
+		v->b = b;
+	    } else
+		s = *pptr;
+	}
+    }
+    *pptr = s;
+    return 0;
+}
+
+
+/**/
+Value
+getvalue(char **pptr, int bracks)
+{
+    char *s, *t;
+    char sav;
+    Value v;
+    int ppar = 0;
+
+    s = t = *pptr;
+    garr = NULL;
+
+    if (idigit(*s))
+	if (bracks >= 0)
+	    ppar = zstrtol(s, &s, 10);
+	else
+	    ppar = *s++ - '0';
+    else if (iident(*s))
+	while (iident(*s))
+	    s++;
+    else if (*s == Quest)
+	*s++ = '?';
+    else if (*s == Pound)
+	*s++ = '#';
+    else if (*s == String)
+	*s++ = '$';
+    else if (*s == Qstring)
+	*s++ = '$';
+    else if (*s == Star)
+	*s++ = '*';
+    else if (*s == '#' || *s == '-' || *s == '?' || *s == '$' ||
+	     *s == '_' || *s == '!' || *s == '@' || *s == '*')
+	s++;
+    else
+	return NULL;
+
+    if ((sav = *s))
+	*s = '\0';
+    if (ppar) {
+	v = (Value) hcalloc(sizeof *v);
+	v->pm = argvparam;
+	v->inv = 0;
+	v->a = v->b = ppar - 1;
+	if (sav)
+	    *s = sav;
+    } else {
+	Param pm;
+	int isvarat;
+
+        isvarat = !strcmp(t, "@");
+	pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t);
+	if (sav)
+	    *s = sav;
+	*pptr = s;
+	if (!pm || (pm->flags & PM_UNSET))
+	    return NULL;
+	v = (Value) hcalloc(sizeof *v);
+	if (PM_TYPE(pm->flags) == PM_ARRAY)
+	    v->isarr = isvarat ? -1 : 1;
+	v->pm = pm;
+	v->inv = 0;
+	v->a = 0;
+	v->b = -1;
+	if (bracks > 0 && (*s == '[' || *s == Inbrack)) {
+	    if (getindex(&s, v)) {
+		*pptr = s;
+		return v;
+	    }
+	} else if (v->isarr && iident(*t) && isset(KSHARRAYS))
+	    v->b = 0, v->isarr = 0;
+    }
+    if (!bracks && *s)
+	return NULL;
+    *pptr = s;
+    if (v->a > MAX_ARRLEN ||
+	v->a < -MAX_ARRLEN) {
+	zerr("subscript to %s: %d", (v->a < 0) ? "small" : "big", v->a);
+	return NULL;
+    }
+    if (v->b > MAX_ARRLEN ||
+	v->b < -MAX_ARRLEN) {
+	zerr("subscript to %s: %d", (v->b < 0) ? "small" : "big", v->b);
+	return NULL;
+    }
+    return v;
+}
+
+/**/
+char *
+getstrvalue(Value v)
+{
+    char *s, **ss;
+    static char buf[(sizeof(long) * 8) + 4];
+
+    if (!v)
+	return hcalloc(1);
+    HEAPALLOC {
+	if (v->inv) {
+	    sprintf(buf, "%d", v->a);
+	    s = dupstring(buf);
+	    LASTALLOC_RETURN s;
+	}
+
+	switch(PM_TYPE(v->pm->flags)) {
+	case PM_ARRAY:
+	    if (v->isarr)
+		s = sepjoin(v->pm->gets.afn(v->pm), NULL);
+	    else {
+		ss = v->pm->gets.afn(v->pm);
+		if (v->a < 0)
+		    v->a += arrlen(ss);
+		s = (v->a >= arrlen(ss) || v->a < 0) ? (char *) hcalloc(1) : ss[v->a];
+	    }
+	    LASTALLOC_RETURN s;
+	case PM_INTEGER:
+	    convbase(s = buf, v->pm->gets.ifn(v->pm), v->pm->ct);
+	    break;
+	case PM_SCALAR:
+	    s = v->pm->gets.cfn(v->pm);
+	    break;
+	default:
+	    s = NULL;
+	    DPUTS(1, "BUG: param node without valid type");
+	    break;
+	}
+
+	if (v->a == 0 && v->b == -1)
+	    LASTALLOC_RETURN s;
+	if (v->a < 0)
+	    v->a += strlen(s);
+	if (v->b < 0)
+	    v->b += strlen(s);
+	s = (v->a > (int)strlen(s)) ? dupstring("") : dupstring(s + v->a);
+	if (v->b < v->a)
+	    s[0] = '\0';
+	else if (v->b - v->a < (int)strlen(s))
+	    s[v->b - v->a + 1 + (s[v->b - v->a] == Meta)] = '\0';
+    } LASTALLOC;
+    return s;
+}
+
+static char *nular[] = {"", NULL};
+
+/**/
+char **
+getarrvalue(Value v)
+{
+    char **s;
+
+    if (!v)
+	return arrdup(nular);
+    if (v->inv) {
+	char buf[DIGBUFSIZE];
+
+	s = arrdup(nular);
+	sprintf(buf, "%d", v->a);
+	s[0] = dupstring(buf);
+	return s;
+    }
+    s = v->pm->gets.afn(v->pm);
+    if (v->a == 0 && v->b == -1)
+	return s;
+    if (v->a < 0)
+	v->a += arrlen(s);
+    if (v->b < 0)
+	v->b += arrlen(s);
+    if (v->a > arrlen(s) || v->a < 0)
+	s = arrdup(nular);
+    else
+	s = arrdup(s) + v->a;
+    if (v->b < v->a)
+	s[0] = NULL;
+    else if (v->b - v->a < arrlen(s))
+	s[v->b - v->a + 1] = NULL;
+    return s;
+}
+
+/**/
+long
+getintvalue(Value v)
+{
+    if (!v || v->isarr)
+	return 0;
+    if (v->inv)
+	return v->a;
+    if (PM_TYPE(v->pm->flags) == PM_INTEGER)
+	return v->pm->gets.ifn(v->pm);
+    return matheval(getstrvalue(v));
+}
+
+/**/
+static void
+setstrvalue(Value v, char *val)
+{
+    char buf[(sizeof(long) * 8) + 4];
+
+    if (v->pm->flags & PM_READONLY) {
+	zerr("read-only variable: %s", v->pm->nam, 0);
+	zsfree(val);
+	return;
+    }
+    if ((v->pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
+	zerr("%s: restricted", v->pm->nam, 0);
+	zsfree(val);
+	return;
+    }
+    switch (PM_TYPE(v->pm->flags)) {
+    case PM_SCALAR:
+	MUSTUSEHEAP("setstrvalue");
+	if (v->a == 0 && v->b == -1) {
+	    (v->pm->sets.cfn) (v->pm, val);
+	    if (v->pm->flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z) && !v->pm->ct)
+		v->pm->ct = strlen(val);
+	} else {
+	    char *z, *x;
+	    int zlen;
+
+	    z = dupstring((v->pm->gets.cfn) (v->pm));
+	    zlen = strlen(z);
+	    if (v->inv && unset(KSHARRAYS))
+		v->a--, v->b--;
+	    if (v->a < 0) {
+		v->a += zlen;
+		if (v->a < 0)
+		    v->a = 0;
+	    }
+	    if (v->a > zlen)
+		v->a = zlen;
+	    if (v->b < 0)
+		v->b += zlen;
+	    if (v->b > zlen - 1)
+		v->b = zlen - 1;
+	    x = (char *) zalloc(v->a + strlen(val) + zlen - v->b);
+	    strncpy(x, z, v->a);
+	    strcpy(x + v->a, val);
+	    strcat(x + v->a, z + v->b + 1);
+	    (v->pm->sets.cfn) (v->pm, x);
+	    zsfree(val);
+	}
+	break;
+    case PM_INTEGER:
+	if (val) {
+	    (v->pm->sets.ifn) (v->pm, matheval(val));
+	    zsfree(val);
+	}
+	if (!v->pm->ct && lastbase != -1)
+	    v->pm->ct = lastbase;
+	break;
+    case PM_ARRAY:
+	MUSTUSEHEAP("setstrvalue");
+	{
+	    char **ss = (char **) zalloc(2 * sizeof(char *));
+
+	    ss[0] = val;
+	    ss[1] = NULL;
+	    setarrvalue(v, ss);
+	}
+	break;
+    }
+    if ((!v->pm->env && !(v->pm->flags & PM_EXPORTED) &&
+	 !(isset(ALLEXPORT) && !v->pm->old)) ||
+	(v->pm->flags & PM_ARRAY) || v->pm->ename)
+	return;
+    if (PM_TYPE(v->pm->flags) == PM_INTEGER)
+	convbase(val = buf, v->pm->gets.ifn(v->pm), v->pm->ct);
+    else
+	val = v->pm->gets.cfn(v->pm);
+    if (v->pm->env)
+	v->pm->env = replenv(v->pm->env, val);
+    else {
+	v->pm->flags |= PM_EXPORTED;
+	v->pm->env = addenv(v->pm->nam, val);
+    }
+}
+
+/**/
+static void
+setintvalue(Value v, long val)
+{
+    char buf[DIGBUFSIZE];
+
+    if (v->pm->flags & PM_READONLY) {
+	zerr("read-only variable: %s", v->pm->nam, 0);
+	return;
+    }
+    if ((v->pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
+	zerr("%s: restricted", v->pm->nam, 0);
+	return;
+    }
+    switch (PM_TYPE(v->pm->flags)) {
+    case PM_SCALAR:
+    case PM_ARRAY:
+	sprintf(buf, "%ld", val);
+	setstrvalue(v, ztrdup(buf));
+	break;
+    case PM_INTEGER:
+	(v->pm->sets.ifn) (v->pm, val);
+	setstrvalue(v, NULL);
+	break;
+    }
+}
+
+/**/
+static void
+setarrvalue(Value v, char **val)
+{
+    if (v->pm->flags & PM_READONLY) {
+	zerr("read-only variable: %s", v->pm->nam, 0);
+	freearray(val);
+	return;
+    }
+    if ((v->pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
+	zerr("%s: restricted", v->pm->nam, 0);
+	freearray(val);
+	return;
+    }
+    if (PM_TYPE(v->pm->flags) != PM_ARRAY) {
+	freearray(val);
+	zerr("attempt to assign array value to non-array", NULL, 0);
+	return;
+    }
+    if (v->a == 0 && v->b == -1)
+	(v->pm->sets.afn) (v->pm, val);
+    else {
+	char **old, **new, **p, **q, **r;
+	int n, ll, i;
+
+	if (v->inv && unset(KSHARRAYS))
+	    v->a--, v->b--;
+	q = old = v->pm->gets.afn(v->pm);
+	n = arrlen(old);
+	if (v->a < 0)
+	    v->a += n;
+	if (v->b < 0)
+	    v->b += n;
+	if (v->a < 0)
+	    v->a = 0;
+	if (v->b < 0)
+	    v->b = 0;
+
+	ll = v->a + arrlen(val);
+	if (v->b < n)
+	    ll += n - v->b;
+
+	p = new = (char **) zcalloc(sizeof(char *) * (ll + 1));
+
+	for (i = 0; i < v->a; i++)
+	    *p++ = i < n ? ztrdup(*q++) : ztrdup("");
+	for (r = val; *r;)
+	    *p++ = ztrdup(*r++);
+	if (v->b + 1 < n)
+	    for (q = old + v->b + 1; *q;)
+		*p++ = ztrdup(*q++);
+	*p = NULL;
+
+	(v->pm->sets.afn) (v->pm, new);
+	freearray(val);
+    }
+}
+
+/* Retrieve an integer parameter */
+
+/**/
+long
+getiparam(char *s)
+{
+    Value v;
+
+    if (!(v = getvalue(&s, 1)))
+	return 0;
+    return getintvalue(v);
+}
+
+/* Retrieve a scalar (string) parameter */
+
+/**/
+char *
+getsparam(char *s)
+{
+    Value v;
+
+    if (!(v = getvalue(&s, 0)))
+	return NULL;
+    return getstrvalue(v);
+}
+
+/* Retrieve an array parameter */
+
+/**/
+char **
+getaparam(char *s)
+{
+    Value v;
+
+    if (!idigit(*s) && (v = getvalue(&s, 0)) &&
+	PM_TYPE(v->pm->flags) == PM_ARRAY)
+	return v->pm->gets.afn(v->pm);
+    return NULL;
+}
+
+/**/
+Param
+setsparam(char *s, char *val)
+{
+    Value v;
+    char *t = s;
+    char *ss;
+
+    if (!isident(s)) {
+	zerr("not an identifier: %s", s, 0);
+	zsfree(val);
+	errflag = 1;
+	return NULL;
+    }
+    if ((ss = strchr(s, '['))) {
+	*ss = '\0';
+	if (!(v = getvalue(&s, 1)))
+	    createparam(t, PM_ARRAY);
+	*ss = '[';
+	v = NULL;
+    } else {
+	if (!(v = getvalue(&s, 1)))
+	    createparam(t, PM_SCALAR);
+	else if (PM_TYPE(v->pm->flags) == PM_ARRAY &&
+		 !(v->pm->flags & PM_SPECIAL) && unset(KSHARRAYS)) {
+	    unsetparam(t);
+	    createparam(t, PM_SCALAR);
+	    v = NULL;
+	}
+    }
+    if (!v && !(v = getvalue(&t, 1))) {
+	zsfree(val);
+	return NULL;
+    }
+    setstrvalue(v, val);
+    return v->pm;
+}
+
+/**/
+Param
+setaparam(char *s, char **val)
+{
+    Value v;
+    char *t = s;
+    char *ss;
+
+    if (!isident(s)) {
+	zerr("not an identifier: %s", s, 0);
+	freearray(val);
+	errflag = 1;
+	return NULL;
+    }
+    if ((ss = strchr(s, '['))) {
+	*ss = '\0';
+	if (!(v = getvalue(&s, 1)))
+	    createparam(t, PM_ARRAY);
+	*ss = '[';
+	v = NULL;
+    } else {
+	if (!(v = getvalue(&s, 1)))
+	    createparam(t, PM_ARRAY);
+	else if (PM_TYPE(v->pm->flags) != PM_ARRAY &&
+		 !(v->pm->flags & PM_SPECIAL)) {
+	    int uniq = v->pm->flags & PM_UNIQUE;
+	    unsetparam(t);
+	    createparam(t, PM_ARRAY | uniq);
+	    v = NULL;
+	}
+    }
+    if (!v)
+	if (!(v = getvalue(&t, 1)))
+	    return NULL;
+    if (isset(KSHARRAYS) && !ss)
+	/* the whole array should be set instead of only the first element */
+	v->b = -1;
+    setarrvalue(v, val);
+    return v->pm;
+}
+
+/**/
+Param
+setiparam(char *s, long val)
+{
+    Value v;
+    char *t = s;
+    Param pm;
+
+    if (!isident(s)) {
+	zerr("not an identifier: %s", s, 0);
+	errflag = 1;
+	return NULL;
+    }
+    if (!(v = getvalue(&s, 1))) {
+	pm = createparam(t, PM_INTEGER);
+	DPUTS(!pm, "BUG: parameter not created");
+	pm->u.val = val;
+	return pm;
+    }
+    setintvalue(v, val);
+    return v->pm;
+}
+
+/* Unset a parameter */
+
+/**/
+void
+unsetparam(char *s)
+{
+    Param pm;
+
+    if ((pm = (Param) paramtab->getnode(paramtab, s)))
+	unsetparam_pm(pm, 0, 1);
+}
+
+/* Unset a parameter */
+
+/**/
+void
+unsetparam_pm(Param pm, int altflag, int exp)
+{
+    Param oldpm, altpm;
+
+    if ((pm->flags & PM_READONLY) && pm->level <= locallevel) {
+	zerr("read-only variable: %s", pm->nam, 0);
+	return;
+    }
+    if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
+	zerr("%s: restricted", pm->nam, 0);
+	return;
+    }
+    pm->unsetfn(pm, exp);
+    if ((pm->flags & PM_EXPORTED) && pm->env) {
+	delenv(pm->env);
+	zsfree(pm->env);
+	pm->env = NULL;
+    }
+
+    /* remove it under its alternate name if necessary */
+    if (pm->ename && !altflag) {
+	altpm = (Param) paramtab->getnode(paramtab, pm->ename);
+	if (altpm)
+	    unsetparam_pm(altpm, 1, exp);
+    }
+
+    /* If this was a local variable, we need to keep the old     *
+     * struct so that it is resurrected at the right level.      *
+     * This is partly because when an array/scalar value is set  *
+     * and the parameter used to be the other sort, unsetparam() *
+     * is called.  Beyond that, there is an ambiguity:  should   *
+     * foo() { local bar; unset bar; } make the global bar       *
+     * available or not?  The following makes the answer "no".   */
+    if (locallevel >= pm->level)
+	return;
+
+    paramtab->removenode(paramtab, pm->nam); /* remove parameter node from table */
+
+    if (pm->old) {
+	oldpm = pm->old;
+	paramtab->addnode(paramtab, oldpm->nam, oldpm);
+	if ((PM_TYPE(oldpm->flags) == PM_SCALAR) && oldpm->sets.cfn == strsetfn)
+	    adduserdir(oldpm->nam, oldpm->u.str, 0, 0);
+    }
+
+    paramtab->freenode((HashNode) pm); /* free parameter node */
+}
+
+/* Standard function to unset a parameter.  This is mostly delegated to *
+ * the specific set function.                                           */
+
+/**/
+void
+stdunsetfn(Param pm, int exp)
+{
+    switch (PM_TYPE(pm->flags)) {
+	case PM_SCALAR: pm->sets.cfn(pm, NULL); break;
+	case PM_ARRAY:  pm->sets.afn(pm, NULL); break;
+    }
+    pm->flags |= PM_UNSET;
+}
+
+/* Function to get value of an integer parameter */
+
+/**/
+static long
+intgetfn(Param pm)
+{
+    return pm->u.val;
+}
+
+/* Function to set value of an integer parameter */
+
+/**/
+static void
+intsetfn(Param pm, long x)
+{
+    pm->u.val = x;
+}
+
+/* Function to get value of a scalar (string) parameter */
+
+/**/
+char *
+strgetfn(Param pm)
+{
+    return pm->u.str ? pm->u.str : (char *) hcalloc(1);
+}
+
+/* Function to set value of a scalar (string) parameter */
+
+/**/
+static void
+strsetfn(Param pm, char *x)
+{
+    zsfree(pm->u.str);
+    pm->u.str = x;
+    adduserdir(pm->nam, x, 0, 0);
+}
+
+/* Function to get value of an array parameter */
+
+/**/
+static char **
+arrgetfn(Param pm)
+{
+    static char *nullarray = NULL;
+
+    return pm->u.arr ? pm->u.arr : &nullarray;
+}
+
+/* Function to set value of an array parameter */
+
+/**/
+static void
+arrsetfn(Param pm, char **x)
+{
+    if (pm->u.arr && pm->u.arr != x)
+	freearray(pm->u.arr);
+    if (pm->flags & PM_UNIQUE)
+	uniqarray(x);
+    pm->u.arr = x;
+}
+
+/* This function is used as the set function for      *
+ * special parameters that cannot be set by the user. */
+
+/**/
+void
+nullsetfn(Param pm, char *x)
+{
+    zsfree(x);
+}
+
+/* Function to get value of generic special integer *
+ * parameter.  data is pointer to global variable   *
+ * containing the integer value.                    */
+
+/**/
+long
+intvargetfn(Param pm)
+{
+    return *((long *)pm->u.data);
+}
+
+/* Function to set value of generic special integer *
+ * parameter.  data is pointer to global variable   *
+ * where the value is to be stored.                 */
+
+/**/
+void
+intvarsetfn(Param pm, long x)
+{
+    *((long *)pm->u.data) = x;
+}
+
+/* Function to set value of any ZLE-related integer *
+ * parameter.  data is pointer to global variable   *
+ * where the value is to be stored.                 */
+
+/**/
+void
+zlevarsetfn(Param pm, long x)
+{
+    if ((long *)pm->u.data == & columns) {
+	if(x <= 0)
+	    x = tccolumns > 0 ? tccolumns : 80;
+	if (x > 2)
+	    termflags &= ~TERM_NARROW;
+	else
+	    termflags |= TERM_NARROW;
+    } else if ((long *)pm->u.data == & lines) {
+	if(x <= 0)
+	    x = tclines > 0 ? tclines : 24;
+	if (x > 2)
+	    termflags &= ~TERM_SHORT;
+	else
+	    termflags |= TERM_SHORT;
+    }
+
+    *((long *)pm->u.data) = x;
+}
+
+/* Function to set value of generic special scalar    *
+ * parameter.  data is pointer to a character pointer *
+ * representing the scalar (string).                  */
+
+/**/
+void
+strvarsetfn(Param pm, char *x)
+{
+    char **q = ((char **)pm->u.data);
+
+    zsfree(*q);
+    *q = x;
+}
+
+/* Function to get value of generic special scalar    *
+ * parameter.  data is pointer to a character pointer *
+ * representing the scalar (string).                  */
+
+/**/
+char *
+strvargetfn(Param pm)
+{
+    char *s = *((char **)pm->u.data);
+
+    if (!s)
+	return hcalloc(1);
+    return s;
+}
+
+/* Function to get value of generic special array  *
+ * parameter.  data is a pointer to the pointer to *
+ * a pointer (a pointer to a variable length array *
+ * of pointers).                                   */
+
+/**/
+char **
+arrvargetfn(Param pm)
+{
+    return *((char ***)pm->u.data);
+}
+
+/* Function to set value of generic special array parameter.    *
+ * data is pointer to a variable length array of pointers which *
+ * represents this array of scalars (strings).  If pm->ename is *
+ * non NULL, then it is a colon separated environment variable  *
+ * version of this array which will need to be updated.         */
+
+/**/
+void
+arrvarsetfn(Param pm, char **x)
+{
+    char ***dptr = (char ***)pm->u.data;
+
+    if (*dptr != x)
+	freearray(*dptr);
+    if (pm->flags & PM_UNIQUE)
+	uniqarray(x);
+    *dptr = x ? x : mkarray(NULL);
+    if (pm->ename && x)
+	arrfixenv(pm->ename, x);
+}
+
+/**/
+char *
+colonarrgetfn(Param pm)
+{
+    return zjoin(*(char ***)pm->u.data, ':');
+}
+
+/**/
+void
+colonarrsetfn(Param pm, char *x)
+{
+    char ***dptr = (char ***)pm->u.data;
+
+    freearray(*dptr);
+    *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) : mkarray(NULL);
+    if (pm->ename)
+	arrfixenv(pm->nam, *dptr);
+    zsfree(x);
+}
+
+/**/
+int
+uniqarray(char **x)
+{
+    int changes = 0;
+    char **t, **p = x;
+
+    if (!x || !*x)
+	return 0;
+    while (*++p)
+	for (t = x; t < p; t++)
+	    if (!strcmp(*p, *t)) {
+		zsfree(*p);
+		for (t = p--; (*t = t[1]) != NULL; t++);
+		changes++;
+		break;
+	    }
+    return changes;
+}
+
+/* Function to get value of special parameter `#' and `ARGC' */
+
+/**/
+long
+poundgetfn(Param pm)
+{
+    return arrlen(pparams);
+}
+
+/* Function to get value for special parameter `RANDOM' */
+
+/**/
+long
+randomgetfn(Param pm)
+{
+    return rand() & 0x7fff;
+}
+
+/* Function to set value of special parameter `RANDOM' */
+
+/**/
+void
+randomsetfn(Param pm, long v)
+{
+    srand((unsigned int)v);
+}
+
+/* Function to get value for special parameter `SECONDS' */
+
+/**/
+long
+secondsgetfn(Param pm)
+{
+    return time(NULL) - shtimer.tv_sec;
+}
+
+/* Function to set value of special parameter `SECONDS' */
+
+/**/
+void
+secondssetfn(Param pm, long x)
+{
+    shtimer.tv_sec = time(NULL) - x;
+    shtimer.tv_usec = 0;
+}
+
+/* Function to get value for special parameter `USERNAME' */
+
+/**/
+char *
+usernamegetfn(Param pm)
+{
+    return get_username();
+}
+
+/* Function to set value of special parameter `USERNAME' */
+
+/**/
+void
+usernamesetfn(Param pm, char *x)
+{
+#if defined(HAVE_SETUID) && defined(HAVE_GETPWNAM)
+    struct passwd *pswd;
+
+    if (x && (pswd = getpwnam(x)) && (pswd->pw_uid != cached_uid)) {
+# ifdef HAVE_INITGROUPS
+	initgroups(x, pswd->pw_gid);
+# endif
+	if(!setgid(pswd->pw_gid) && !setuid(pswd->pw_uid)) {
+	    zsfree(cached_username);
+	    cached_username = ztrdup(pswd->pw_name);
+	    cached_uid = pswd->pw_uid;
+	}
+    }
+#endif /* HAVE_SETUID && HAVE_GETPWNAM */
+}
+
+/* Function to get value for special parameter `UID' */
+
+/**/
+long
+uidgetfn(Param pm)
+{
+    return getuid();
+}
+
+/* Function to set value of special parameter `UID' */
+
+/**/
+void
+uidsetfn(Param pm, uid_t x)
+{
+#ifdef HAVE_SETUID
+    setuid(x);
+#endif
+}
+
+/* Function to get value for special parameter `EUID' */
+
+/**/
+long
+euidgetfn(Param pm)
+{
+    return geteuid();
+}
+
+/* Function to set value of special parameter `EUID' */
+
+/**/
+void
+euidsetfn(Param pm, uid_t x)
+{
+#ifdef HAVE_SETEUID
+    seteuid(x);
+#endif
+}
+
+/* Function to get value for special parameter `GID' */
+
+/**/
+long
+gidgetfn(Param pm)
+{
+    return getgid();
+}
+
+/* Function to set value of special parameter `GID' */
+
+/**/
+void
+gidsetfn(Param pm, gid_t x)
+{
+#ifdef HAVE_SETUID
+    setgid(x);
+#endif
+}
+
+/* Function to get value for special parameter `EGID' */
+
+/**/
+long
+egidgetfn(Param pm)
+{
+    return getegid();
+}
+
+/* Function to set value of special parameter `EGID' */
+
+/**/
+void
+egidsetfn(Param pm, gid_t x)
+{
+#ifdef HAVE_SETEUID
+    setegid(x);
+#endif
+}
+
+/**/
+long
+ttyidlegetfn(Param pm)
+{
+    struct stat ttystat;
+
+    if (SHTTY == -1 || fstat(SHTTY, &ttystat))
+	return -1;
+    return time(NULL) - ttystat.st_atime;
+}
+
+/* Function to get value for special parameter `IFS' */
+
+/**/
+char *
+ifsgetfn(Param pm)
+{
+    return ifs;
+}
+
+/* Function to set value of special parameter `IFS' */
+
+/**/
+void
+ifssetfn(Param pm, char *x)
+{
+    zsfree(ifs);
+    ifs = x;
+    inittyptab();
+}
+
+/* Functions to set value of special parameters `LANG' and `LC_*' */
+
+#ifdef LC_ALL
+static struct localename {
+    char *name;
+    int category;
+} lc_names[] = {
+#ifdef LC_COLLATE
+    {"LC_COLLATE", LC_COLLATE},
+#endif
+#ifdef LC_CTYPE
+    {"LC_CTYPE", LC_CTYPE},
+#endif
+#ifdef LC_MESSAGES
+    {"LC_MESSAGES", LC_MESSAGES},
+#endif
+#ifdef LC_TIME
+    {"LC_TIME", LC_TIME},
+#endif
+    {NULL, 0}
+};
+
+/**/
+static void
+setlang(char *x)
+{
+    struct localename *ln;
+
+    setlocale(LC_ALL, x ? x : "");
+    for (ln = lc_names; ln->name; ln++)
+	if ((x = getsparam(ln->name)))
+	    setlocale(ln->category, x);
+}
+
+/**/
+void
+lc_allsetfn(Param pm, char *x)
+{
+    strsetfn(pm, x);
+    if (!x)
+	setlang(getsparam("LANG"));
+    else
+	setlocale(LC_ALL, x);
+}
+
+/**/
+void
+langsetfn(Param pm, char *x)
+{
+    strsetfn(pm, x);
+    setlang(x);
+}
+
+/**/
+void
+lcsetfn(Param pm, char *x)
+{
+    struct localename *ln;
+
+    strsetfn(pm, x);
+    if (getsparam("LC_ALL"))
+	return;
+    if (!x)
+	x = getsparam("LANG");
+
+    for (ln = lc_names; ln->name; ln++)
+	if (!strcmp(ln->name, pm->nam))
+	    setlocale(ln->category, x ? x : "");
+}
+#endif
+
+/* Function to get value for special parameter `HISTSIZE' */
+
+/**/
+long
+histsizegetfn(Param pm)
+{
+    return histsiz;
+}
+
+/* Function to set value of special parameter `HISTSIZE' */
+
+/**/
+void
+histsizesetfn(Param pm, long v)
+{
+    if ((histsiz = v) <= 2)
+	histsiz = 2;
+    resizehistents();
+}
+
+/* Function to get value for special parameter `ERRNO' */
+
+/**/
+long
+errnogetfn(Param pm)
+{
+    return errno;
+}
+
+/* Function to get value for special parameter `histchar' */
+
+/**/
+char *
+histcharsgetfn(Param pm)
+{
+    static char buf[4];
+
+    buf[0] = bangchar;
+    buf[1] = hatchar;
+    buf[2] = hashchar;
+    buf[3] = '\0';
+    return buf;
+}
+
+/* Function to set value of special parameter `histchar' */
+
+/**/
+void
+histcharssetfn(Param pm, char *x)
+{
+    if (x) {
+	bangchar = x[0];
+	hatchar = (bangchar) ? x[1] : '\0';
+	hashchar = (hatchar) ? x[2] : '\0';
+	zsfree(x);
+    } else {
+	bangchar = '!';
+	hashchar = '#';
+	hatchar = '^';
+    }
+    inittyptab();
+}
+
+/* Function to get value for special parameter `HOME' */
+
+/**/
+char *
+homegetfn(Param pm)
+{
+    return home;
+}
+
+/* Function to set value of special parameter `HOME' */
+
+/**/
+void
+homesetfn(Param pm, char *x)
+{
+    zsfree(home);
+    if (x && isset(CHASELINKS) && (home = xsymlink(x)))
+	zsfree(x);
+    else
+	home = x ? x : ztrdup("");
+    finddir(NULL);
+}
+
+/* Function to get value for special parameter `WORDCHARS' */
+
+/**/
+char *
+wordcharsgetfn(Param pm)
+{
+    return wordchars;
+}
+
+/* Function to set value of special parameter `WORDCHARS' */
+
+/**/
+void
+wordcharssetfn(Param pm, char *x)
+{
+    zsfree(wordchars);
+    wordchars = x;
+    inittyptab();
+}
+
+/* Function to get value for special parameter `_' */
+
+/**/
+char *
+underscoregetfn(Param pm)
+{
+    return underscore;
+}
+
+/* Function to get value for special parameter `TERM' */
+
+/**/
+char *
+termgetfn(Param pm)
+{
+    return term;
+}
+
+/* Function to set value of special parameter `TERM' */
+
+/**/
+void
+termsetfn(Param pm, char *x)
+{
+    zsfree(term);
+    term = x ? x : ztrdup("");
+
+    /* If non-interactive, delay setting up term till we need it. */
+    if (unset(INTERACTIVE) || !*term)
+	termflags |= TERM_UNKNOWN;
+    else 
+	init_term();
+}
+
+/* We could probably replace the replenv with the actual code to *
+ * do the replacing, since we've already scanned for the string. */
+
+/**/
+static void
+arrfixenv(char *s, char **t)
+{
+    char **ep, *u;
+    int len_s;
+    Param pm;
+
+    MUSTUSEHEAP("arrfixenv");
+    if (t == path)
+	cmdnamtab->emptytable(cmdnamtab);
+    u = zjoin(t, ':');
+    len_s = strlen(s);
+    pm = (Param) paramtab->getnode(paramtab, s);
+    for (ep = environ; *ep; ep++)
+	if (!strncmp(*ep, s, len_s) && (*ep)[len_s] == '=') {
+	    pm->env = replenv(*ep, u);
+	    return;
+	}
+    if (isset(ALLEXPORT))
+	pm->flags |= PM_EXPORTED;
+    if (pm->flags & PM_EXPORTED)
+	pm->env = addenv(s, u);
+}
+
+/* Given *name = "foo", it searchs the environment for string *
+ * "foo=bar", and returns a pointer to the beginning of "bar" */
+
+/**/
+char *
+zgetenv(char *name)
+{
+    char **ep, *s, *t;
+ 
+    for (ep = environ; *ep; ep++) {
+	for (s = *ep, t = name; *s && *s == *t; s++, t++);
+	if (*s == '=' && !*t)
+	    return s + 1;
+    }
+    return NULL;
+}
+
+/* Change the value of an existing environment variable */
+
+/**/
+char *
+replenv(char *e, char *value)
+{
+    char **ep, *s;
+    int len_value;
+
+    for (ep = environ; *ep; ep++)
+	if (*ep == e) {
+	    for (len_value = 0, s = value;
+		 *s && (*s++ != Meta || *s++ != 32); len_value++);
+	    s = e;
+	    while (*s++ != '=');
+	    *ep = (char *) zrealloc(e, s - e + len_value + 1);
+	    s = s - e + *ep - 1;
+	    while (*s++)
+		if ((*s = *value++) == Meta)
+		    *s = *value++ ^ 32;
+	    return *ep;
+	}
+    return NULL;
+}
+
+/* Given strings *name = "foo", *value = "bar", *
+ * return a new string *str = "foo=bar".        */
+
+/**/
+static char *
+mkenvstr(char *name, char *value)
+{
+    char *str, *s;
+    int len_name, len_value;
+
+    len_name = strlen(name);
+    for (len_value = 0, s = value;
+	 *s && (*s++ != Meta || *s++ != 32); len_value++);
+    s = str = (char *) zalloc(len_name + len_value + 2);
+    strcpy(s, name);
+    s += len_name;
+    *s = '=';
+    while (*s++)
+	if ((*s = *value++) == Meta)
+	    *s = *value++ ^ 32;
+    return str;
+}
+
+/* Given *name = "foo", *value = "bar", add the    *
+ * string "foo=bar" to the environment.  Return a  *
+ * pointer to the location of this new environment *
+ * string.                                         */
+
+/**/
+char *
+addenv(char *name, char *value)
+{
+    char **ep, *s, *t;
+    int num_env;
+
+    /* First check if there is already an environment *
+     * variable matching string `name'.               */
+    for (ep = environ; *ep; ep++) {
+	for (s = *ep, t = name; *s && *s == *t; s++, t++);
+	if (*s == '=' && !*t) {
+	    zsfree(*ep);
+	    return *ep = mkenvstr(name, value);
+	}
+    }
+
+    /* Else we have to make room and add it */
+    num_env = arrlen(environ);
+    environ = (char **) zrealloc(environ, (sizeof(char *)) * (num_env + 2));
+
+    /* Now add it at the end */
+    ep = environ + num_env;
+    *ep = mkenvstr(name, value);
+    *(ep + 1) = NULL;
+    return *ep;
+}
+
+/* Delete a pointer from the list of pointers to environment *
+ * variables by shifting all the other pointers up one slot. */
+
+/**/
+void
+delenv(char *x)
+{
+    char **ep;
+
+    for (ep = environ; *ep; ep++) {
+	if (*ep == x)
+	    break;
+    }
+    if (*ep)
+	for (; (ep[0] = ep[1]); ep++);
+}
+
+/**/
+static void
+convbase(char *s, long v, int base)
+{
+    int digs = 0;
+    unsigned long x;
+
+    if (v < 0)
+	*s++ = '-', v = -v;
+    if (base <= 1)
+	base = 10;
+
+    if (base != 10) {
+	sprintf(s, "%d#", base);
+	s += strlen(s);
+    }
+    for (x = v; x; digs++)
+	x /= base;
+    if (!digs)
+	digs = 1;
+    s[digs--] = '\0';
+    x = v;
+    while (digs >= 0) {
+	int dig = x % base;
+
+	s[digs--] = (dig < 10) ? '0' + dig : dig - 10 + 'A';
+	x /= base;
+    }
+}
+
+/* Start a parameter scope */
+
+/**/
+void
+startparamscope(void)
+{
+    locallevel++;
+}
+
+/* End a parameter scope: delete the parameters local to the scope. */
+
+/**/
+void
+endparamscope(void)
+{
+    locallevel--;
+    scanhashtable(paramtab, 0, 0, 0, scanendscope, 0);
+}
+
+/**/
+static void
+scanendscope(HashNode hn, int flags)
+{
+    Param pm = (Param)hn;
+    if(pm->level > locallevel)
+	unsetparam_pm(pm, 0, 0);
+}