summaryrefslogtreecommitdiff
path: root/Src/params.c
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:05:38 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:05:38 +0000
commite74702b467171dbdafb56dfe354794a212e020d9 (patch)
treec295b3e9b2e93e2de10331877442615b0f37e779 /Src/params.c
parentc175751b501a3a4cb40ad4787340a597ea769be4 (diff)
downloadzsh-e74702b467171dbdafb56dfe354794a212e020d9.tar.gz
zsh-e74702b467171dbdafb56dfe354794a212e020d9.zip
Initial revision
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);
+}