about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Src/compat.c102
-rw-r--r--Src/params.c958
-rw-r--r--zshconfig.ac2
4 files changed, 734 insertions, 335 deletions
diff --git a/ChangeLog b/ChangeLog
index bd58572b0..97eae18b4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,14 @@
+2001-07-02  Oliver Kiddle  <opk@zsh.org>
+
+	* 15204: zshconfig.ac, Src/compat.c, Src/params.c: fix compilation
+	problems on IRIX 5.2 and correct error messages
+
 2001-06-30  Bart Schaefer  <schaefer@zsh.org>
 
 	* 15191: Src/init.c: Don't infinite loop if there's an error in
 	the user's precmd -- reset errflag after preprompt().
 
-001-06-29  Andrej Borsenkow  <bor@zsh.org>
+2001-06-29  Andrej Borsenkow  <bor@zsh.org>
 
 	* 15183: zshconfig.ac: fix DLLD in non-ELF case
 
diff --git a/Src/compat.c b/Src/compat.c
index a7e4069d8..86611a727 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -105,6 +105,108 @@ strerror(int errnum)
 #endif
 
 
+#if 0
+/* pathconf(_PC_PATH_MAX) is not currently useful to zsh.  The value *
+ * returned varies depending on a number of factors, e.g. the amount *
+ * of memory available to the operating system at a given time; thus *
+ * it can't be used for buffer allocation, or even as an indication  *
+ * of whether an attempt to use or create a given pathname may fail  *
+ * at any future time.                                               *
+ *                                                                   *
+ * The call is also permitted to fail if the argument path is not an *
+ * existing directory, so even to make sense of that one must search *
+ * for a valid directory somewhere in the path and adjust.  Even if  *
+ * it succeeds, the return value is relative to the input directory, *
+ * and therefore potentially relative to the length of the shortest  *
+ * path either to that directory or to our working directory.        *
+ *                                                                   *
+ * Finally, see the note below for glibc; detection of pathconf() is *
+ * not by itself an indication that it works reliably.               */
+
+/* The documentation for pathconf() says something like:             *
+ *     The limit is returned, if one exists.  If the system  does    *
+ *     not  have  a  limit  for  the  requested  resource,  -1 is    *
+ *     returned, and errno is unchanged.  If there is  an  error,    *
+ *     -1  is returned, and errno is set to reflect the nature of    *
+ *     the error.                                                    *
+ *                                                                   *
+ * System calls are not permitted to set errno to 0; but we must (or *
+ * some other flag value) in order to determine that the resource is *
+ * unlimited.  What use is leaving errno unchanged?  Instead, define *
+ * a wrapper that resets errno to 0 and returns 0 for "the system    *
+ * does not have a limit," so that -1 always means a real error.     */
+
+/**/
+mod_export long
+zpathmax(char *dir)
+{
+#ifdef HAVE_PATHCONF
+    long pathmax;
+
+    errno = 0;
+    if ((pathmax = pathconf(dir, _PC_PATH_MAX)) >= 0) {
+	/* Some versions of glibc pathconf return a hardwired value! */
+	return pathmax;
+    } else if (errno == EINVAL || errno == ENOENT || errno == ENOTDIR) {
+	/* Work backward to find a directory, until we run out of path. */
+	char *tail = strrchr(dir, '/');
+	while (tail > dir && tail[-1] == '/')
+	    --tail;
+	if (tail > dir) {
+	    *tail = 0;
+	    pathmax = zpathmax(dir);
+	    *tail = '/';
+	} else {
+	    errno = 0;
+	    if (tail)
+		pathmax = pathconf("/", _PC_PATH_MAX);
+	    else
+		pathmax = pathconf(".", _PC_PATH_MAX);
+	}
+	if (pathmax > 0) {
+	    long taillen = (tail ? strlen(tail) : (strlen(dir) + 1));
+	    if (taillen < pathmax)
+		return pathmax - taillen;
+	    else
+		errno = ENAMETOOLONG;
+	}
+    }
+    if (errno)
+	return -1;
+    else
+	return 0; /* pathmax should be considered unlimited */
+#else
+    long dirlen = strlen(dir);
+
+    /* The following is wrong if dir is not an absolute path. */
+    return ((long) ((dirlen >= PATH_MAX) ?
+		    ((errno = ENAMETOOLONG), -1) :
+		    ((errno = 0), PATH_MAX - dirlen)));
+#endif
+}
+#endif /* 0 */
+
+#ifdef HAVE_SYSCONF
+/* This is replaced by a macro from system.h if not HAVE_SYSCONF.    *
+ * 0 is returned by sysconf if _SC_OPEN_MAX is unavailable;          *
+ * -1 is returned on error                                           *
+ *                                                                   *
+ * Neither of these should happen, but resort to OPEN_MAX rather     *
+ * than return 0 or -1 just in case.                                 */
+
+/**/
+mod_export long
+zopenmax(void)
+{
+    long openmax = sysconf(_SC_OPEN_MAX);
+
+    if (openmax < 1)
+	return OPEN_MAX;
+    else
+	return openmax;
+}
+#endif
+
 /**/
 mod_export char *
 zgetdir(struct dirsav *d)
diff --git a/Src/params.c b/Src/params.c
index cdaecb199..e0a572e62 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -56,7 +56,6 @@ char **path,		/* $path        */
 /**/
 char *argzero,		/* $0           */
      *home,		/* $HOME        */
-     *hostnam,		/* $HOST        */
      *nullcmd,		/* $NULLCMD     */
      *oldpwd,		/* $OLDPWD      */
      *zoptarg,		/* $OPTARG      */
@@ -67,13 +66,13 @@ char *argzero,		/* $0           */
      *readnullcmd,	/* $READNULLCMD */
      *rprompt,		/* $RPROMPT     */
      *sprompt,		/* $SPROMPT     */
-     *term,		/* $TERM        */
      *wordchars,	/* $WORDCHARS   */
      *zsh_name;		/* $ZSH_NAME    */
 /**/
 mod_export
 char *ifs,		/* $IFS         */
      *postedit,		/* $POSTEDIT    */
+     *term,		/* $TERM        */
      *ttystrname,	/* $TTY         */
      *pwd;		/* $PWD         */
 
@@ -134,8 +133,8 @@ 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("#", poundgetfn, nullintsetfn, PM_READONLY),
+IPDEF1("ERRNO", errnogetfn, nullintsetfn, PM_READONLY),
 IPDEF1("GID", gidgetfn, gidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("EGID", egidgetfn, egidsetfn, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("HISTSIZE", histsizegetfn, histsizesetfn, PM_RESTRICTED),
@@ -143,17 +142,17 @@ 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),
+IPDEF1("TTYIDLE", ttyidlegetfn, nullintsetfn, 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("-", dashgetfn, nullstrsetfn, 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),
+IPDEF2("_", underscoregetfn, nullstrsetfn, PM_READONLY),
 
 #ifdef USE_LOCALE
 # define LCIPDEF(name) IPDEF2(name, strgetfn, lcsetfn, PM_UNSET)
@@ -176,7 +175,7 @@ LCIPDEF("LC_TIME"),
 # endif
 #endif /* USE_LOCALE */
 
-#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}
+#define IPDEF4(A,B) {NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL,BR((void *)B),SFN(nullintsetfn),GFN(intvargetfn),stdunsetfn,10,NULL,NULL,NULL,0}
 IPDEF4("!", &lastpid),
 IPDEF4("$", &mypid),
 IPDEF4("?", &lastval),
@@ -220,10 +219,11 @@ IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED),
 IPDEF9("*", &pparams, NULL),
 IPDEF9("@", &pparams, NULL),
 {NULL, NULL},
+#define IPDEF10(A,B,C) {NULL,A,PM_ARRAY|PM_SPECIAL,BR(NULL),SFN(C),GFN(B),stdunsetfn,10,NULL,NULL,NULL,0}
 
 /* The following parameters are not avaible in sh/ksh compatibility *
  * mode. All of these has sh compatible equivalents.                */
-IPDEF1("ARGC", poundgetfn, nullsetfn, PM_READONLY),
+IPDEF1("ARGC", poundgetfn, nullintsetfn, PM_READONLY),
 IPDEF2("HISTCHARS", histcharsgetfn, histcharssetfn, PM_DONTIMPORT),
 IPDEF4("status", &lastval),
 IPDEF7("prompt", &prompt),
@@ -244,6 +244,8 @@ IPDEF9("watch", &watch, "WATCH"),
 IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED),
 IPDEF9F("path", &path, "PATH", PM_RESTRICTED),
 
+IPDEF10("pipestatus", pipestatgetfn, pipestatsetfn),
+
 {NULL, NULL}
 };
 #undef BR
@@ -263,7 +265,10 @@ mod_export HashTable paramtab, realparamtab;
 mod_export HashTable
 newparamtable(int size, char const *name)
 {
-    HashTable ht = newhashtable(size, name, NULL);
+    HashTable ht;
+    if (!size)
+	size = 17;
+    ht = newhashtable(size, name, NULL);
 
     ht->hash        = hasher;
     ht->emptytable  = emptyhashtable;
@@ -391,8 +396,8 @@ scanparamvals(HashNode hn, int flags)
     }
     v.isarr = (PM_TYPE(v.pm->flags) & (PM_ARRAY|PM_HASHED));
     v.inv = 0;
-    v.a = 0;
-    v.b = -1;
+    v.start = 0;
+    v.end = -1;
     paramvals[numparamvals] = getstrvalue(&v);
     if (flags & SCANPM_MATCHVAL) {
 	if (pattry(scanprog, paramvals[numparamvals])) {
@@ -434,13 +439,39 @@ getvaluearr(Value v)
     else if (PM_TYPE(v->pm->flags) == PM_HASHED) {
 	v->arr = paramvalarr(v->pm->gets.hfn(v->pm), v->isarr);
 	/* Can't take numeric slices of associative arrays */
-	v->a = 0;
-	v->b = numparamvals;
+	v->start = 0;
+	v->end = numparamvals + 1;
 	return v->arr;
     } else
 	return NULL;
 }
 
+/*
+ * Split environment string into (name, vlaue) pair.
+ * this is used to avoid in-place editing of environment table
+ * that results in core dump on some systems
+ */
+
+static int
+split_env_string(char *env, char **name, char **value)
+{
+    char *str, *tenv;
+
+    if (!env || !name || !value)
+	return 0;
+
+    tenv = strcpy(zhalloc(strlen(env) + 1), env);
+    for (str = tenv; *str && *str != '='; str++)
+	;
+    if (str != tenv && *str == '=') {
+	*str = '\0';
+	*name = tenv;
+	*value = str + 1;
+	return 1;
+    } else
+	return 0;
+}
+    
 /* Set up parameter hash table.  This will add predefined  *
  * parameter entries as well as setting up parameter table *
  * entries for environment variables we inherit.           */
@@ -450,9 +481,13 @@ void
 createparamtable(void)
 {
     Param ip, pm;
-    char **new_environ, **envp, **envp2, **sigptr, **t;
-    char buf[50], *str, *iname;
-    int num_env, oae = opts[ALLEXPORT];
+#ifndef HAVE_PUTENV
+    char **new_environ;
+    int  envsize;
+#endif
+    char **envp, **envp2, **sigptr, **t;
+    char buf[50], *str, *iname, *ivalue, *hostnam;
+    int  oae = opts[ALLEXPORT];
 #ifdef HAVE_UNAME
     struct utsname unamebuf;
     char *machinebuf;
@@ -487,41 +522,48 @@ createparamtable(void)
     setsparam("TMPPREFIX", ztrdup(DEFAULT_TMPPREFIX));
     setsparam("TIMEFMT", ztrdup(DEFAULT_TIMEFMT));
     setsparam("WATCHFMT", ztrdup(default_watchfmt));
+
+    hostnam = (char *)zalloc(256);
+    gethostname(hostnam, 256);
     setsparam("HOST", ztrdup(hostnam));
+    zfree(hostnam, 256);
+
     setsparam("LOGNAME", ztrdup((str = getlogin()) && *str ? str : cached_username));
 
+#ifndef HAVE_PUTENV
     /* 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;
+    envsize = sizeof(char *)*(1 + arrlen(environ));
+    new_environ = (char **) zalloc(envsize);
+    memcpy(new_environ, environ, envsize);
+    environ = new_environ;
+#endif
+
+    /* Use heap allocation to avoid many small alloc/free calls */
+    pushheap();
 
     /* 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;
+     * into the parameter hash table. Copy them into dynamic   *
+     * memory so that we can free them if needed               */
+    for (envp = envp2 = environ; *envp2; envp2++) {
+	if (split_env_string(*envp2, &iname, &ivalue)) {
+	    if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) {
 		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_DONTIMPORT || pm->flags & PM_EXPORTED)) &&
+		    (pm = setsparam(iname, metafy(ivalue, -1, META_DUP)))) {
 		    pm->flags |= PM_EXPORTED;
-		    pm->env = *envp++ = ztrdup(*envp2);
-		    *envp = NULL;
 		    if (pm->flags & PM_SPECIAL)
-			pm->env = replenv(pm->env, getsparam(pm->nam),
-					  pm->flags);
+			pm->env = mkenvstr (pm->nam,
+					    getsparam(pm->nam), pm->flags);
+		    else
+			pm->env = ztrdup(*envp2);
+		    *envp++ = pm->env;
 		}
 	    }
-	    *str = '=';
 	}
     }
-    environ = new_environ;
+    popheap();
+    *envp = '\0';
     opts[ALLEXPORT] = oae;
 
     pm = (Param) paramtab->getnode(paramtab, "HOME");
@@ -543,16 +585,17 @@ createparamtable(void)
     /* Add the standard non-special parameters */
     set_pwd_env();
 #ifdef HAVE_UNAME
-    if(uname(&unamebuf)) setsparam("MACHTYPE", ztrdup(MACHTYPE));
+    if(uname(&unamebuf)) setsparam("CPUTYPE", ztrdup("unknown"));
     else
     {
        machinebuf = ztrdup(unamebuf.machine);
-       setsparam("MACHTYPE", machinebuf);
+       setsparam("CPUTYPE", machinebuf);
     }
 	
 #else
-    setsparam("MACHTYPE", ztrdup(MACHTYPE));
+    setsparam("CPUTYPE", ztrdup("unknown"));
 #endif
+    setsparam("MACHTYPE", ztrdup(MACHTYPE));
     setsparam("OSTYPE", ztrdup(OSTYPE));
     setsparam("TTY", ztrdup(ttystrname));
     setsparam("VENDOR", ztrdup(VENDOR));
@@ -613,6 +656,9 @@ createparam(char *name, int flags)
 {
     Param pm, oldpm;
 
+    if (paramtab != realparamtab)
+	flags = (flags & ~PM_EXPORTED) | PM_HASHELEM;
+
     if (name != nulstring) {
 	oldpm = (Param) (paramtab == realparamtab ?
 			 gethashnode2(paramtab, name) :
@@ -623,6 +669,12 @@ createparam(char *name, int flags)
 	if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) {
 	    if (!(oldpm->flags & PM_UNSET) || (oldpm->flags & PM_SPECIAL)) {
 		oldpm->flags &= ~PM_UNSET;
+		if ((oldpm->flags & PM_SPECIAL) && oldpm->ename) {
+		    Param altpm = 
+			(Param) paramtab->getnode(paramtab, oldpm->ename);
+		    if (altpm)
+			altpm->flags &= ~PM_UNSET;
+		}
 		return NULL;
 	    }
 	    if ((oldpm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
@@ -636,16 +688,23 @@ createparam(char *name, int flags)
 	} else {
 	    pm = (Param) zcalloc(sizeof *pm);
 	    if ((pm->old = oldpm)) {
-		/* needed to avoid freeing oldpm */
+		/*
+		 * needed to avoid freeing oldpm, but we do take it
+		 * out of the environment when it's hidden.
+		 */
+		if (oldpm->env) {
+		    delenv(oldpm->env);
+		    oldpm->env = NULL;
+		}
 		paramtab->removenode(paramtab, name);
 	    }
 	    paramtab->addnode(paramtab, ztrdup(name), pm);
 	}
 
-	if (isset(ALLEXPORT) && !oldpm)
+	if (isset(ALLEXPORT) && !(flags & PM_HASHELEM))
 	    flags |= PM_EXPORTED;
     } else {
-	pm = (Param) zhalloc(sizeof *pm);
+	pm = (Param) hcalloc(sizeof *pm);
 	pm->nam = nulstring;
     }
     pm->flags = flags & ~PM_LOCAL;
@@ -668,6 +727,7 @@ copyparam(Param tpm, Param pm, int toplevel)
      * with sets.?fn() usage).
      */
     tpm->flags = pm->flags;
+    tpm->ct = pm->ct;
     if (!toplevel)
 	tpm->flags &= ~PM_SPECIAL;
     switch (PM_TYPE(pm->flags)) {
@@ -712,43 +772,30 @@ isident(char *s)
     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 0
-    /* 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;
+    if (idigit(*s)) {
+	/* If the first character is `s' is a digit, then all must be */
+	for (ss = ++s; *ss; ss++)
+	    if (!idigit(*ss))
+		break;
+    } else {
+	/* Find the first character in `s' not in the iident type table */
+	for (ss = s; *ss; ss++)
+	    if (!iident(*ss))
+		break;
+    }
 
-    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;
-#else
     /* If the next character is not [, then it is *
-     * definitely not a valid identifier.              */
+     * definitely not a valid identifier.         */
     if (!*ss)
 	return 1;
     if (*ss != '[')
 	return 0;
 
-    /* Require balanced [ ] pairs */
-    if (skipparens('[', ']', &ss))
+    /* Require balanced [ ] pairs with something between */
+    if (!(ss = parse_subscript(++ss, 1)))
 	return 0;
-    return !*ss;
-#endif
+    untokenize(s);
+    return !ss[1];
 }
 
 /**/
@@ -805,7 +852,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		sep = "\n";
 		break;
 	    case 'e':
-		/* obsolate compatibility flag without any real effect */
+		/* Compatibility flag with no effect except to prevent *
+		 * special interpretation by getindex() of `*' or `@'. */
 		break;
 	    case 'n':
 		t = get_strarg(++s);
@@ -836,7 +884,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		break;
 	    case 's':
 		/* This gives the string that separates words *
-		 * (for use with the `w' flag.                */
+		 * (for use with the `w' flag).               */
 		t = get_strarg(++s);
 		if (!*t)
 		    goto flagerr;
@@ -880,8 +928,24 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
     }
 
     for (t = s, i = 0;
-	 (c = *t) && ((c != ']' && c != Outbrack &&
+	 (c = *t) && ((c != Outbrack &&
 		       (ishash || c != ',')) || i); t++) {
+	/* Untokenize INULL() except before brackets and double-quotes */
+	if (INULL(c)) {
+	    c = t[1];
+	    if (c == '[' || c == ']' ||
+		c == '(' || c == ')' ||
+		c == '{' || c == '}') {
+		/* This test handles nested subscripts in hash keys */
+		if (ishash && i)
+		    *t = ztokens[*t - Pound];
+		needtok = 1;
+		++t;
+	    } else if (c != '"')
+		*t = ztokens[*t - Pound];
+	    continue;
+	}
+	/* Inbrack and Outbrack are probably never found here ... */
 	if (c == '[' || c == Inbrack)
 	    i++;
 	else if (c == ']' || c == Outbrack)
@@ -893,11 +957,18 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 	return 0;
     s = dupstrpfx(s, t - s);
     *str = tt = t;
+    /* If we're NOT reverse subscripting, strip the INULL()s so brackets *
+     * are not backslashed after parsestr().  Otherwise leave them alone *
+     * so that the brackets will be escaped when we patcompile() or when *
+     * subscript arithmetic is performed (for nested subscripts).        */
+    if (ishash && (keymatch || !rev))
+	remnulargs(s);
     if (needtok) {
 	if (parsestr(s))
 	    return 0;
 	singsub(&s);
-    }
+    } else if (rev)
+	remnulargs(s);	/* This is probably always a no-op, but ... */
     if (!rev) {
 	if (ishash) {
 	    HashTable ht = v->pm->gets.hfn(v->pm);
@@ -913,13 +984,15 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		paramtab = tht;
 	    }
 	    v->isarr = (*inv ? SCANPM_WANTINDEX : 0);
-	    v->a = 0;
+	    v->start = 0;
 	    *inv = 0;	/* We've already obtained the "index" (key) */
-	    *w = v->b = -1;
+	    *w = v->end = -1;
 	    r = isset(KSHARRAYS) ? 1 : 0;
-	} else
-	if (!(r = mathevalarg(s, &s)) || (isset(KSHARRAYS) && r >= 0))
-	    r++;
+	} else {
+	    r = mathevalarg(s, &s);
+	    if (isset(KSHARRAYS) && r >= 0)
+		r++;
+	}
 	if (word && !v->isarr) {
 	    s = t = getstrvalue(v);
 	    i = wordcount(s, sep, 0);
@@ -936,7 +1009,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		return 0;
 
 	    if (!a2 && *tt != ',')
-		*w = (zlong)(s - t) - 1;
+		*w = (zlong)(s - t);
 
 	    return (a2 ? s : d + 1) - t;
 	} else if (!v->isarr && !word) {
@@ -964,7 +1037,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		    s = d;
 		}
 	    } else {
-		if (!l || s[l - 1] != '*') {
+		if (!l || s[l - 1] != '*' || (l > 1 && s[l - 2] == '\\')) {
 		    d = (char *) hcalloc(l + 2);
 		    strcpy(d, s);
 		    strcat(d, "*");
@@ -972,7 +1045,10 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		}
 	    }
 	}
-	tokenize(s);
+	if (!keymatch) {
+	    tokenize(s);
+	    remnulargs(s);
+	}
 
 	if (keymatch || (pprog = patcompile(s, 0, NULL))) {
 	    int len;
@@ -981,10 +1057,9 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		if (ishash) {
 		    scanprog = pprog;
 		    scanstr = s;
-		    if (keymatch) {
-			untokenize(s);
+		    if (keymatch)
 			v->isarr |= SCANPM_KEYMATCH;
-		    } else if (ind)
+		    else if (ind)
 			v->isarr |= SCANPM_MATCHKEY;
 		    else
 			v->isarr |= SCANPM_MATCHVAL;
@@ -995,7 +1070,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 				 (v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL |
 					      SCANPM_KEYMATCH))))) {
 			*inv = v->inv;
-			*w = v->b;
+			*w = v->end;
 			return 1;
 		    }
 		} else
@@ -1046,7 +1121,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		    if (!--r) {
 			r = (zlong)(t - s + (a2 ? -1 : 1));
 			if (!a2 && *tt != ',')
-			    *w = r + strlen(ta[i]) - 2;
+			    *w = r + strlen(ta[i]) - 1;
 			return r;
 		    }
 		return a2 ? -1 : 0;
@@ -1058,10 +1133,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		if (beg < 0)
 		    beg += len;
 		if (beg >= 0 && beg < len) {
+                    char *de = d + len;
+
 		    if (a2) {
 			if (down) {
 			    if (!hasbeg)
-				beg = len - 1;
+				beg = len;
 			    for (r = beg, t = d + beg; t >= d; r--, t--) {
 				sav = *t;
 				*t = '\0';
@@ -1073,7 +1150,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 				*t = sav;
 			    }
 			} else
-			    for (r = beg, t = d + beg; *t; r++, t++) {
+			    for (r = beg, t = d + beg; t <= de; r++, t++) {
 				sav = *t;
 				*t = '\0';
 				if (pattry(pprog, d) &&
@@ -1086,20 +1163,20 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 		    } else {
 			if (down) {
 			    if (!hasbeg)
-				beg = len - 1;
+				beg = len;
 			    for (r = beg + 1, t = d + beg; t >= d; r--, t--) {
 				if (pattry(pprog, t) &&
 				    !--num)
 				    return r;
 			    }
 			} else
-			    for (r = beg + 1, t = d + beg; *t; r++, t++)
+			    for (r = beg + 1, t = d + beg; t <= de; r++, t++)
 				if (pattry(pprog, t) &&
 				    !--num)
 				    return r;
 		    }
 		}
-		return 0;
+		return down ? 0 : len + 1;
 	    }
 	}
     }
@@ -1108,81 +1185,96 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w)
 
 /**/
 int
-getindex(char **pptr, Value v)
+getindex(char **pptr, Value v, int dq)
 {
-    int a, b, inv = 0;
+    int start, end, inv = 0;
     char *s = *pptr, *tbrack;
 
     *s++ = '[';
-    for (tbrack = s; *tbrack && *tbrack != ']' && *tbrack != Outbrack; tbrack++)
-	if (itok(*tbrack))
+    s = parse_subscript(s, dq);	/* Error handled after untokenizing */
+    /* Now we untokenize everthing except INULL() markers so we can check *
+     * for the '*' and '@' special subscripts.  The INULL()s are removed  *
+     * in getarg() after we know whether we're doing reverse indexing.    */
+    for (tbrack = *pptr + 1; *tbrack && tbrack != s; tbrack++) {
+	if (INULL(*tbrack) && !*++tbrack)
+	    break;
+	if (itok(*tbrack))	/* Need to check for Nularg here? */
 	    *tbrack = ztokens[*tbrack - Pound];
-    if (*tbrack == Outbrack)
-	*tbrack = ']';
-    if ((s[0] == '*' || s[0] == '@') && s[1] == ']') {
+    }
+    /* If we reached the end of the string (s == NULL) we have an error */
+    if (*tbrack)
+	*tbrack = Outbrack;
+    else {
+	zerr("invalid subscript", NULL, 0);
+	*pptr = tbrack;
+	return 1;
+    }
+    s = *pptr + 1;
+    if ((s[0] == '*' || s[0] == '@') && s + 1 == tbrack) {
 	if ((v->isarr || IS_UNSET_VALUE(v)) && s[0] == '@')
 	    v->isarr |= SCANPM_ISVAR_AT;
-	v->a = 0;
-	v->b = -1;
+	v->start = 0;
+	v->end = -1;
 	s += 2;
     } else {
 	zlong we = 0, dummy;
 
-	a = getarg(&s, &inv, v, 0, &we);
+	start = getarg(&s, &inv, v, 0, &we);
 
 	if (inv) {
-	    if (!v->isarr && a != 0) {
+	    if (!v->isarr && start != 0) {
 		char *t, *p;
 		t = getstrvalue(v);
-		if (a > 0) {
-		    for (p = t + a - 1; p-- > t; )
+		if (start > 0) {
+		    for (p = t + start - 1; p-- > t; )
 			if (*p == Meta)
-			    a--;
+			    start--;
 		} else
-		    a = -ztrlen(t + a + strlen(t));
+		    start = -ztrlen(t + start + strlen(t));
 	    }
-	    if (a > 0 && (isset(KSHARRAYS) || (v->pm->flags & PM_HASHED)))
-		a--;
+	    if (start > 0 && (isset(KSHARRAYS) || (v->pm->flags & PM_HASHED)))
+		start--;
 	    if (v->isarr != SCANPM_WANTINDEX) {
 		v->inv = 1;
 		v->isarr = 0;
-		v->a = v->b = a;
+		v->start = start;
+		v->end = start + 1;
 	    }
 	    if (*s == ',') {
 		zerr("invalid subscript", NULL, 0);
-		while (*s != ']' && *s != Outbrack)
-		    s++;
-		*pptr = s;
+		*tbrack = ']';
+		*pptr = tbrack+1;
 		return 1;
 	    }
-	    if (*s == ']' || *s == Outbrack)
+	    if (s == tbrack)
 		s++;
 	} else {
 	    int com;
 
-	    if (a > 0)
-		a--;
 	    if ((com = (*s == ','))) {
 		s++;
-		b = getarg(&s, &inv, v, 1, &dummy);
-		if (b > 0)
-		    b--;
+		end = getarg(&s, &inv, v, 1, &dummy);
 	    } else {
-		b = we ? we : a;
+		end = we ? we : start;
 	    }
-	    if (*s == ']' || *s == Outbrack) {
+	    if (start > 0)
+		start--;
+	    else if (start == 0 && end == 0)
+		end++;
+	    if (s == tbrack) {
 		s++;
-		if (v->isarr && a == b && !com &&
+		if (v->isarr && start == end-1 && !com &&
 		    (!(v->isarr & SCANPM_MATCHMANY) ||
 		     !(v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL |
 				   SCANPM_KEYMATCH))))
 		    v->isarr = 0;
-		v->a = a;
-		v->b = b;
+		v->start = start;
+		v->end = end;
 	    } else
 		s = *pptr;
 	}
     }
+    *tbrack = ']';
     *pptr = s;
     return 0;
 }
@@ -1239,7 +1331,8 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 	    v = (Value) hcalloc(sizeof *v);
 	v->pm = argvparam;
 	v->inv = 0;
-	v->a = v->b = ppar - 1;
+	v->start = ppar - 1;
+	v->end = ppar;
 	if (sav)
 	    *s = sav;
     } else {
@@ -1269,28 +1362,34 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 	}
 	v->pm = pm;
 	v->inv = 0;
-	v->a = 0;
-	v->b = -1;
+	v->start = 0;
+	v->end = -1;
 	if (bracks > 0 && (*s == '[' || *s == Inbrack)) {
-	    if (getindex(&s, v)) {
+	    if (getindex(&s, v, (flags & SCANPM_DQUOTED))) {
 		*pptr = s;
 		return v;
 	    }
 	} else if (!(flags & SCANPM_ASSIGNING) && v->isarr &&
 		   iident(*t) && isset(KSHARRAYS))
-	    v->b = 0, v->isarr = 0;
+	    v->end = 1, v->isarr = 0;
     }
     if (!bracks && *s)
 	return NULL;
     *pptr = s;
-    if (v->a > MAX_ARRLEN ||
-	v->a < -MAX_ARRLEN) {
-	zerr("subscript too %s: %d", (v->a < 0) ? "small" : "big", v->a);
+    if (v->start > MAX_ARRLEN) {
+	zerr("subscript too %s: %d", "big", v->start + !isset(KSHARRAYS));
+	return NULL;
+    }
+    if (v->start < -MAX_ARRLEN) {
+	zerr("subscript too %s: %d", "small", v->start);
 	return NULL;
     }
-    if (v->b > MAX_ARRLEN ||
-	v->b < -MAX_ARRLEN) {
-	zerr("subscript too %s: %d", (v->b < 0) ? "small" : "big", v->b);
+    if (v->end > MAX_ARRLEN+1) {
+	zerr("subscript too %s: %d", "big", v->end - !!isset(KSHARRAYS));
+	return NULL;
+    }
+    if (v->end < -MAX_ARRLEN) {
+	zerr("subscript too %s: %d", "small", v->end);
 	return NULL;
     }
     return v;
@@ -1301,13 +1400,13 @@ mod_export char *
 getstrvalue(Value v)
 {
     char *s, **ss;
-    char buf[(sizeof(zlong) * 8) + 4];
+    char buf[BDIGBUFSIZE];
 
     if (!v)
 	return hcalloc(1);
 
     if (v->inv && !(v->pm->flags & PM_HASHED)) {
-	sprintf(buf, "%d", v->a);
+	sprintf(buf, "%d", v->start);
 	s = dupstring(buf);
 	return s;
     }
@@ -1317,7 +1416,7 @@ getstrvalue(Value v)
 	/* (!v->isarr) should be impossible unless emulating ksh */
 	if (!v->isarr && emulation == EMULATE_KSH) {
 	    s = dupstring("[0]");
-	    if (getindex(&s, v) == 0)
+	    if (getindex(&s, v, 0) == 0)
 		s = getstrvalue(v);
 	    return s;
 	} /* else fall through */
@@ -1326,9 +1425,10 @@ getstrvalue(Value v)
 	if (v->isarr)
 	    s = sepjoin(ss, NULL, 1);
 	else {
-	    if (v->a < 0)
-		v->a += arrlen(ss);
-	    s = (v->a >= arrlen(ss) || v->a < 0) ? (char *) hcalloc(1) : ss[v->a];
+	    if (v->start < 0)
+		v->start += arrlen(ss);
+	    s = (v->start >= arrlen(ss) || v->start < 0) ?
+		(char *) hcalloc(1) : ss[v->start];
 	}
 	return s;
     case PM_INTEGER:
@@ -1348,18 +1448,18 @@ getstrvalue(Value v)
 	break;
     }
 
-    if (v->a == 0 && v->b == -1)
+    if (v->start == 0 && v->end == -1)
 	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)
+    if (v->start < 0)
+	v->start += strlen(s);
+    if (v->end < 0)
+	v->end += strlen(s) + 1;
+    s = (v->start > (int)strlen(s)) ? dupstring("") : dupstring(s + v->start);
+    if (v->end <= v->start)
 	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';
+    else if (v->end - v->start <= (int)strlen(s))
+	s[v->end - v->start + (s[v->end - v->start - 1] == Meta)] = '\0';
 
     return s;
 }
@@ -1367,7 +1467,7 @@ getstrvalue(Value v)
 static char *nular[] = {"", NULL};
 
 /**/
-char **
+mod_export char **
 getarrvalue(Value v)
 {
     char **s;
@@ -1380,25 +1480,25 @@ getarrvalue(Value v)
 	char buf[DIGBUFSIZE];
 
 	s = arrdup(nular);
-	sprintf(buf, "%d", v->a);
+	sprintf(buf, "%d", v->start);
 	s[0] = dupstring(buf);
 	return s;
     }
     s = getvaluearr(v);
-    if (v->a == 0 && v->b == -1)
+    if (v->start == 0 && v->end == -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)
+    if (v->start < 0)
+	v->start += arrlen(s);
+    if (v->end < 0)
+	v->end += arrlen(s) + 1;
+    if (v->start > arrlen(s) || v->start < 0)
 	s = arrdup(nular);
     else
-	s = arrdup(s + v->a);
-    if (v->b < v->a)
+	s = arrdup(s + v->start);
+    if (v->end <= v->start)
 	s[0] = NULL;
-    else if (v->b - v->a < arrlen(s))
-	s[v->b - v->a + 1] = NULL;
+    else if (v->end - v->start <= arrlen(s))
+	s[v->end - v->start] = NULL;
     return s;
 }
 
@@ -1409,7 +1509,7 @@ getintvalue(Value v)
     if (!v || v->isarr)
 	return 0;
     if (v->inv)
-	return v->a;
+	return v->start;
     if (PM_TYPE(v->pm->flags) == PM_INTEGER)
 	return v->pm->gets.ifn(v->pm);
     if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT))
@@ -1427,7 +1527,7 @@ getnumvalue(Value v)
     if (!v || v->isarr) {
 	mn.u.l = 0;
     } else if (v->inv) {
-	mn.u.l = v->a;
+	mn.u.l = v->start;
     } else if (PM_TYPE(v->pm->flags) == PM_INTEGER) {
 	mn.u.l = v->pm->gets.ifn(v->pm);
     } else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) {
@@ -1439,11 +1539,39 @@ getnumvalue(Value v)
 }
 
 /**/
+void
+export_param(Param pm)
+{
+    char buf[BDIGBUFSIZE], *val;
+
+    if (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED)) {
+#if 0	/* Requires changes elsewhere in params.c and builtin.c */
+	if (emulation == EMULATE_KSH /* isset(KSHARRAYS) */) {
+	    struct value v;
+	    v.isarr = 1;
+	    v.inv = 0;
+	    v.start = 0;
+	    v.end = -1;
+	    val = getstrvalue(&v);
+	} else
+#endif
+	    return;
+    } else if (PM_TYPE(pm->flags) == PM_INTEGER)
+	convbase(val = buf, pm->gets.ifn(pm), pm->ct);
+    else if (pm->flags & (PM_EFLOAT|PM_FFLOAT))
+	val = convfloat(pm->gets.ffn(pm), pm->ct,
+			pm->flags, NULL);
+    else
+	val = pm->gets.cfn(pm);
+
+    pm->flags |= PM_EXPORTED;
+    pm->env = addenv(pm->nam, val, pm->flags);
+}
+
+/**/
 mod_export void
 setstrvalue(Value v, char *val)
 {
-    char buf[(sizeof(zlong) * 8) + 4];
-
     if (v->pm->flags & PM_READONLY) {
 	zerr("read-only variable: %s", v->pm->nam, 0);
 	zsfree(val);
@@ -1454,10 +1582,14 @@ setstrvalue(Value v, char *val)
 	zsfree(val);
 	return;
     }
+    if (v->pm->flags & PM_HASHED) {
+	zerr("%s: attempt to set slice of associative array", v->pm->nam, 0);
+	return;
+    }
     v->pm->flags &= ~PM_UNSET;
     switch (PM_TYPE(v->pm->flags)) {
     case PM_SCALAR:
-	if (v->a == 0 && v->b == -1) {
+	if (v->start == 0 && v->end == -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);
@@ -1468,22 +1600,22 @@ setstrvalue(Value v, char *val)
 	    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;
+		v->start--, v->end--;
+	    if (v->start < 0) {
+		v->start += zlen;
+		if (v->start < 0)
+		    v->start = 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);
+	    if (v->start > zlen)
+		v->start = zlen;
+	    if (v->end < 0)
+		v->end += zlen + 1;
+	    else if (v->end > zlen)
+		v->end = zlen;
+	    x = (char *) zalloc(v->start + strlen(val) + zlen - v->end + 1);
+	    strncpy(x, z, v->start);
+	    strcpy(x + v->start, val);
+	    strcat(x + v->start, z + v->end);
 	    (v->pm->sets.cfn) (v->pm, x);
 	    zsfree(val);
 	}
@@ -1516,29 +1648,17 @@ setstrvalue(Value v, char *val)
 	break;
     }
     if ((!v->pm->env && !(v->pm->flags & PM_EXPORTED) &&
-	 !(isset(ALLEXPORT) && !v->pm->old)) ||
+	 !(isset(ALLEXPORT) && !(v->pm->flags & PM_HASHELEM))) ||
 	(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 if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT))
-	val = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct,
-			v->pm->flags, NULL);
-    else
-	val = v->pm->gets.cfn(v->pm);
-    if (v->pm->env)
-	v->pm->env = replenv(v->pm->env, val, v->pm->flags);
-    else {
-	v->pm->flags |= PM_EXPORTED;
-	v->pm->env = addenv(v->pm->nam, val, v->pm->flags);
-    }
+    export_param(v->pm);
 }
 
 /**/
 void
 setnumvalue(Value v, mnumber val)
 {
-    char buf[DIGBUFSIZE], *p;
+    char buf[BDIGBUFSIZE], *p;
 
     if (v->pm->flags & PM_READONLY) {
 	zerr("read-only variable: %s", v->pm->nam, 0);
@@ -1551,9 +1671,11 @@ setnumvalue(Value v, mnumber val)
     switch (PM_TYPE(v->pm->flags)) {
     case PM_SCALAR:
     case PM_ARRAY:
-	if (val.type & MN_INTEGER)
-	    convbase(p = buf, val.u.l, 0);
-	else
+	if ((val.type & MN_INTEGER) || outputradix) {
+	    if (!(val.type & MN_INTEGER))
+		val.u.l = (zlong) val.u.d;
+	    convbase(p = buf, val.u.l, outputradix);
+	} else
 	    p = convfloat(val.u.d, 0, 0, NULL);
 	setstrvalue(v, ztrdup(p));
 	break;
@@ -1587,10 +1709,11 @@ setarrvalue(Value v, char **val)
     }
     if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED))) {
 	freearray(val);
-	zerr("attempt to assign array value to non-array", NULL, 0);
+	zerr("%s: attempt to assign array value to non-array",
+	     v->pm->nam, 0);
 	return;
     }
-    if (v->a == 0 && v->b == -1) {
+    if (v->start == 0 && v->end == -1) {
 	if (PM_TYPE(v->pm->flags) == PM_HASHED)
 	    arrhashsetfn(v->pm, val);
 	else
@@ -1601,34 +1724,42 @@ setarrvalue(Value v, char **val)
 
 	if ((PM_TYPE(v->pm->flags) == PM_HASHED)) {
 	    freearray(val);
-	    zerr("attempt to set slice of associative array", NULL, 0);
+	    zerr("%s: attempt to set slice of associative array",
+		 v->pm->nam, 0);
 	    return;
 	}
-	if (v->inv && unset(KSHARRAYS))
-	    v->a--, v->b--;
+	if (v->inv && unset(KSHARRAYS)) {
+	    if (v->start > 0)
+		v->start--;
+	    v->end--;
+	}
+	if (v->end < v->start)
+	    v->end = v->start;
 	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;
+	if (v->start < 0) {
+	    v->start += n;
+	    if (v->start < 0)
+		v->start = 0;
+	}
+	if (v->end < 0) {
+	    v->end += n + 1;
+	    if (v->end < 0)
+		v->end = 0;
+	}
+
+	ll = v->start + arrlen(val);
+	if (v->end <= n)
+	    ll += n - v->end + 1;
 
 	p = new = (char **) zcalloc(sizeof(char *) * (ll + 1));
 
-	for (i = 0; i < v->a; i++)
+	for (i = 0; i < v->start; 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;)
+	if (v->end < n)
+	    for (q = old + v->end; *q;)
 		*p++ = ztrdup(*q++);
 	*p = NULL;
 
@@ -1713,6 +1844,21 @@ gethparam(char *s)
     return NULL;
 }
 
+/* Retrieve the keys of an assoc array parameter as an array */
+
+/**/
+mod_export char **
+gethkparam(char *s)
+{
+    struct value vbuf;
+    Value v;
+
+    if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) &&
+	PM_TYPE(v->pm->flags) == PM_HASHED)
+	return paramvalarr(v->pm->gets.hfn(v->pm), SCANPM_WANTKEYS);
+    return NULL;
+}
+
 /**/
 mod_export Param
 setsparam(char *s, char *val)
@@ -1728,6 +1874,7 @@ setsparam(char *s, char *val)
 	errflag = 1;
 	return NULL;
     }
+    queue_signals();
     if ((ss = strchr(s, '['))) {
 	*ss = '\0';
 	if (!(v = getvalue(&vbuf, &s, 1)))
@@ -1745,10 +1892,12 @@ setsparam(char *s, char *val)
 	}
     }
     if (!v && !(v = getvalue(&vbuf, &t, 1))) {
+	unqueue_signals();
 	zsfree(val);
 	return NULL;
     }
     setstrvalue(v, val);
+    unqueue_signals();
     return v->pm;
 }
 
@@ -1767,13 +1916,16 @@ setaparam(char *s, char **val)
 	errflag = 1;
 	return NULL;
     }
+    queue_signals();
     if ((ss = strchr(s, '['))) {
 	*ss = '\0';
 	if (!(v = getvalue(&vbuf, &s, 1)))
 	    createparam(t, PM_ARRAY);
 	*ss = '[';
 	if (v && PM_TYPE(v->pm->flags) == PM_HASHED) {
-	    zerr("attempt to set slice of associative array", NULL, 0);
+	    unqueue_signals();
+	    zerr("%s: attempt to set slice of associative array",
+		 v->pm->nam, 0);
 	    freearray(val);
 	    errflag = 1;
 	    return NULL;
@@ -1791,9 +1943,12 @@ setaparam(char *s, char **val)
 	}
     }
     if (!v)
-	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING)))
+	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) {
+	    unqueue_signals();
 	    return NULL;
+	}
     setarrvalue(v, val);
+    unqueue_signals();
     return v->pm;
 }
 
@@ -1816,20 +1971,23 @@ sethparam(char *s, char **val)
 	zerr("nested associative arrays not yet supported", NULL, 0);
 	errflag = 1;
 	return NULL;
-    } else {
-	if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING)))
-	    createparam(t, PM_HASHED);
-	else if (!(PM_TYPE(v->pm->flags) & PM_HASHED) &&
-		 !(v->pm->flags & PM_SPECIAL)) {
-	    unsetparam(t);
-	    createparam(t, PM_HASHED);
-	    v = NULL;
-	}
+    }
+    queue_signals();
+    if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING)))
+	createparam(t, PM_HASHED);
+    else if (!(PM_TYPE(v->pm->flags) & PM_HASHED) &&
+	     !(v->pm->flags & PM_SPECIAL)) {
+	unsetparam(t);
+	createparam(t, PM_HASHED);
+	v = NULL;
     }
     if (!v)
-	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING)))
+	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) {
+	    unqueue_signals();
 	    return NULL;
+	}
     setarrvalue(v, val);
+    unqueue_signals();
     return v->pm;
 }
 
@@ -1839,7 +1997,7 @@ setiparam(char *s, zlong val)
 {
     struct value vbuf;
     Value v;
-    char *t = s;
+    char *t = s, *ss;
     Param pm;
     mnumber mnval;
 
@@ -1848,15 +2006,25 @@ setiparam(char *s, zlong val)
 	errflag = 1;
 	return NULL;
     }
+    queue_signals();
     if (!(v = getvalue(&vbuf, &s, 1))) {
-	pm = createparam(t, PM_INTEGER);
+	if ((ss = strchr(s, '[')))
+	    *ss = '\0';
+	if (!(pm = createparam(t, ss ? PM_ARRAY : PM_INTEGER)))
+	    pm = (Param) paramtab->getnode(paramtab, t);
 	DPUTS(!pm, "BUG: parameter not created");
-	pm->u.val = val;
-	return pm;
+	if (ss) {
+	    *ss = '[';
+	} else {
+	    pm->ct = outputradix;
+	}
+	v = getvalue(&vbuf, &t, 1);
+	DPUTS(!v, "BUG: value not found for new parameter");
     }
     mnval.type = MN_INTEGER;
     mnval.u.l = val;
     setnumvalue(v, mnval);
+    unqueue_signals();
     return v->pm;
 }
 
@@ -1871,7 +2039,7 @@ setnparam(char *s, mnumber val)
 {
     struct value vbuf;
     Value v;
-    char *t = s;
+    char *t = s, *ss = NULL;
     Param pm;
 
     if (!isident(s)) {
@@ -1879,17 +2047,25 @@ setnparam(char *s, mnumber val)
 	errflag = 1;
 	return NULL;
     }
+    queue_signals();
     if (!(v = getvalue(&vbuf, &s, 1))) {
-	pm = createparam(t, (val.type & MN_INTEGER) ? PM_INTEGER
-			 : PM_FFLOAT);
+	if ((ss = strchr(s, '[')))
+	    *ss = '\0';
+	pm = createparam(t, ss ? PM_ARRAY :
+			 (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT);
+	if (!pm)
+	    pm = (Param) paramtab->getnode(paramtab, t);
 	DPUTS(!pm, "BUG: parameter not created");
-	if (val.type & MN_INTEGER)
-	    pm->u.val = val.u.l;
-	else
-	    pm->u.dval = val.u.d;
-	return pm;
+	if (ss) {
+	    *ss = '[';
+	} else if (val.type & MN_INTEGER) {
+	    pm->ct = outputradix;
+	}
+	v = getvalue(&vbuf, &t, 1);
+	DPUTS(!v, "BUG: value not found for new parameter");
     }
     setnumvalue(v, val);
+    unqueue_signals();
     return v->pm;
 }
 
@@ -1901,10 +2077,12 @@ unsetparam(char *s)
 {
     Param pm;
 
+    queue_signals();
     if ((pm = (Param) (paramtab == realparamtab ?
 		       gethashnode2(paramtab, s) :
 		       paramtab->getnode(paramtab, s))))
 	unsetparam_pm(pm, 0, 1);
+    unqueue_signals();
 }
 
 /* Unset a parameter */
@@ -1926,7 +2104,6 @@ unsetparam_pm(Param pm, int altflag, int exp)
     pm->unsetfn(pm, exp);
     if ((pm->flags & PM_EXPORTED) && pm->env) {
 	delenv(pm->env);
-	zsfree(pm->env);
 	pm->env = NULL;
     }
 
@@ -1960,8 +2137,18 @@ unsetparam_pm(Param pm, int altflag, int exp)
 	oldpm = pm->old;
 	paramtab->addnode(paramtab, oldpm->nam, oldpm);
 	if ((PM_TYPE(oldpm->flags) == PM_SCALAR) &&
+	    !(pm->flags & PM_HASHELEM) &&
 	    oldpm->sets.cfn == strsetfn)
 	    adduserdir(oldpm->nam, oldpm->u.str, 0, 0);
+	if (oldpm->flags & PM_EXPORTED) {
+	    /*
+	     * Re-export the old value which we removed in typeset_single().
+	     * I don't think we need to test for ALL_EXPORT here, since if
+	     * it was used to export the parameter originally the parmeter
+	     * should still have the PM_EXPORTED flag.
+	     */
+	    export_param(oldpm);
+	}
     }
 
     paramtab->freenode((HashNode) pm); /* free parameter node */
@@ -1985,7 +2172,7 @@ stdunsetfn(Param pm, int exp)
 /* Function to get value of an integer parameter */
 
 /**/
-static zlong
+mod_export zlong
 intgetfn(Param pm)
 {
     return pm->u.val;
@@ -2035,7 +2222,8 @@ strsetfn(Param pm, char *x)
 {
     zsfree(pm->u.str);
     pm->u.str = x;
-    adduserdir(pm->nam, x, 0, 0);
+    if (!(pm->flags & PM_HASHELEM))
+	adduserdir(pm->nam, x, 0, 0);
 }
 
 /* Function to get value of an array parameter */
@@ -2098,7 +2286,7 @@ arrhashsetfn(Param pm, char **val)
     HashTable opmtab = paramtab, ht = 0;
     char **aptr = val;
     Value v = (Value) hcalloc(sizeof *v);
-    v->b = -1;
+    v->end = -1;
 
     if (alen % 2) {
 	freearray(val);
@@ -2126,16 +2314,25 @@ arrhashsetfn(Param pm, char **val)
     free(val);		/* not freearray() */
 }
 
-/* This function is used as the set function for      *
- * special parameters that cannot be set by the user. */
+/*
+ * These functions are used as the set function for special parameters that
+ * cannot be set by the user.  The set is incomplete as the only such
+ * parameters are scalar and integer.
+ */
 
 /**/
 void
-nullsetfn(Param pm, char *x)
+nullstrsetfn(Param pm, char *x)
 {
     zsfree(x);
 }
 
+/**/
+void
+nullintsetfn(Param pm, zlong x)
+{}
+
+
 /* Function to get value of generic special integer *
  * parameter.  data is pointer to global variable   *
  * containing the integer value.                    */
@@ -2264,23 +2461,20 @@ colonarrsetfn(Param pm, char *x)
 }
 
 /**/
-int
+void
 uniqarray(char **x)
 {
-    int changes = 0;
     char **t, **p = x;
 
     if (!x || !*x)
-	return 0;
+	return;
     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' */
@@ -2358,6 +2552,7 @@ usernamesetfn(Param pm, char *x)
 	}
     }
 #endif /* HAVE_SETUID && HAVE_GETPWNAM */
+    zsfree(x);
 }
 
 /* Function to get value for special parameter `UID' */
@@ -2503,9 +2698,11 @@ setlang(char *x)
     struct localename *ln;
 
     setlocale(LC_ALL, x ? x : "");
+    queue_signals();
     for (ln = lc_names; ln->name; ln++)
 	if ((x = getsparam(ln->name)))
 	    setlocale(ln->category, x);
+    unqueue_signals();
 }
 
 /**/
@@ -2513,8 +2710,11 @@ void
 lc_allsetfn(Param pm, char *x)
 {
     strsetfn(pm, x);
-    if (!x)
+    if (!x) {
+	queue_signals();
 	setlang(getsparam("LANG"));
+	unqueue_signals();
+    }
     else
 	setlocale(LC_ALL, x);
 }
@@ -2536,12 +2736,14 @@ lcsetfn(Param pm, char *x)
     strsetfn(pm, x);
     if (getsparam("LC_ALL"))
 	return;
+    queue_signals();
     if (!x)
 	x = getsparam("LANG");
 
     for (ln = lc_names; ln->name; ln++)
 	if (!strcmp(ln->name, pm->nam))
 	    setlocale(ln->category, x ? x : "");
+    unqueue_signals();
 }
 #endif /* USE_LOCALE */
 
@@ -2688,37 +2890,119 @@ termsetfn(Param pm, char *x)
 	init_term();
 }
 
-/* We could probably replace the replenv with the actual code to *
- * do the replacing, since we've already scanned for the string. */
+/* Function to get value for special parameter `pipestatus' */
+
+/**/
+static char **
+pipestatgetfn(Param pm)
+{
+    char **x = (char **) zhalloc((numpipestats + 1) * sizeof(char *));
+    char buf[20], **p;
+    int *q, i;
+
+    for (p = x, q = pipestats, i = numpipestats; i--; p++, q++) {
+	sprintf(buf, "%d", *q);
+	*p = dupstring(buf);
+    }
+    *p = NULL;
+
+    return x;
+}
+
+/* Function to get value for special parameter `pipestatus' */
 
 /**/
 static void
+pipestatsetfn(Param pm, char **x)
+{
+    if (x) {
+        int i;
+
+        for (i = 0; *x && i < MAX_PIPESTATS; i++, x++)
+            pipestats[i] = atoi(*x);
+        numpipestats = i;
+    }
+    else
+        numpipestats = 0;
+}
+
+/**/
+void
 arrfixenv(char *s, char **t)
 {
-    char **ep, *u;
-    int len_s;
     Param pm;
 
+    if (t == path)
+	cmdnamtab->emptytable(cmdnamtab);
+
     pm = (Param) paramtab->getnode(paramtab, s);
+    
     /*
      * Only one level of a parameter can be exported.  Unless
      * ALLEXPORT is set, this must be global.
      */
-    if (t == path)
-	cmdnamtab->emptytable(cmdnamtab);
-    if (isset(ALLEXPORT) ? !!pm->old : pm->level)
+
+    if (pm->flags & PM_HASHELEM)
 	return;
-    u = t ? zjoin(t, ':', 1) : "";
-    len_s = strlen(s);
-    for (ep = environ; *ep; ep++)
-	if (!strncmp(*ep, s, len_s) && (*ep)[len_s] == '=') {
-	    pm->env = replenv(*ep, u, pm->flags);
-	    return;
-	}
+
     if (isset(ALLEXPORT))
 	pm->flags |= PM_EXPORTED;
+
+    /*
+     * Do not "fix" parameters that were not exported
+     */
+
     if (pm->flags & PM_EXPORTED)
-	pm->env = addenv(s, u, pm->flags);
+	pm->env = addenv(s, t ? zjoin(t, ':', 1) : "", pm->flags);
+}
+
+
+static int
+zputenv(char *str)
+{
+#ifdef HAVE_PUTENV
+    return putenv(str);
+#else
+    char **ep;
+    int num_env;
+
+
+    /* First check if there is already an environment *
+     * variable matching string `name'.               */
+    if (findenv(str, &num_env)) {
+	environ[num_env] = str;
+    } else {
+    /* 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 = str;
+	*(ep + 1) = NULL;
+    }
+    return 0;
+#endif
+}
+
+/**/
+static int
+findenv(char *name, int *pos)
+{
+    char **ep, *eq;
+    int  nlen;
+
+
+    eq = strchr(name, '=');
+    nlen = eq ? eq - name : strlen(name);
+    for (ep = environ; *ep; ep++) 
+	if (!strncmp (*ep, name, nlen) && *((*ep)+nlen) == '=') {
+	    if (pos)
+		*pos = ep - environ;
+	    return 1;
+	}
+    
+    return 0;
 }
 
 /* Given *name = "foo", it searchs the environment for string *
@@ -2728,14 +3012,18 @@ arrfixenv(char *s, char **t)
 mod_export char *
 zgetenv(char *name)
 {
+#ifdef HAVE_GETENV
+    return getenv(name);
+#else
     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;
+       for (s = *ep, t = name; *s && *s == *t; s++, t++);
+       if (*s == '=' && !*t)
+           return s + 1;
     }
     return NULL;
+#endif
 }
 
 /**/
@@ -2752,29 +3040,43 @@ copyenvstr(char *s, char *value, int flags)
     }
 }
 
-/* Change the value of an existing environment variable */
-
 /**/
 char *
-replenv(char *e, char *value, int flags)
-{
-    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;
-	    copyenvstr(s, value, flags);
-	    return *ep;
-	}
-    return NULL;
+addenv(char *name, char *value, int flags)
+{
+    char *oldenv = 0, *newenv = 0, *env = 0;
+    int pos;
+
+    /* First check if there is already an environment *
+     * variable matching string `name'. If not, and   *
+     * we are not requested to add new, return        */
+    if (findenv(name, &pos))
+	oldenv = environ[pos];
+
+     newenv = mkenvstr(name, value, flags);
+     if (zputenv(newenv)) {
+        zsfree(newenv);
+	return NULL;
+    }
+    /*
+     * Under Cygwin we must use putenv() to maintain consistency.
+     * Unfortunately, current version (1.1.2) copies argument and may
+     * silently reuse exisiting environment string. This tries to
+     * check for both cases
+     */
+    if (findenv(name, &pos)) {
+	env = environ[pos];
+	if (env != oldenv)
+	    zsfree(oldenv);
+	if (env != newenv)
+	    zsfree(newenv);
+	return env;
+    }
+
+    return NULL; /* Cannot happen */
 }
 
+
 /* Given strings *name = "foo", *value = "bar", *
  * return a new string *str = "foo=bar".        */
 
@@ -2801,33 +3103,6 @@ mkenvstr(char *name, char *value, int flags)
  * pointer to the location of this new environment *
  * string.                                         */
 
-/**/
-char *
-addenv(char *name, char *value, int flags)
-{
-    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, flags);
-	}
-    }
-
-    /* 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, flags);
-    *(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. */
@@ -2842,8 +3117,10 @@ delenv(char *x)
 	if (*ep == x)
 	    break;
     }
-    if (*ep)
+    if (*ep) {
 	for (; (ep[0] = ep[1]); ep++);
+    }
+    zsfree(x);
 }
 
 /**/
@@ -2855,13 +3132,21 @@ convbase(char *s, zlong v, int base)
 
     if (v < 0)
 	*s++ = '-', v = -v;
-    if (base <= 1)
-	base = 10;
-
-    if (base != 10) {
-	sprintf(s, "%d#", base);
+    if (base >= -1 && base <= 1)
+	base = -10;
+
+    if (base > 0) {
+	if (isset(CBASES) && base == 16)
+	    sprintf(s, "0x");
+	else if (isset(CBASES) && base == 8 && isset(OCTALZEROES))
+	    sprintf(s, "0");
+	else if (base != 10)
+	    sprintf(s, "%d#", base);
+	else
+	    *s = 0;
 	s += strlen(s);
-    }
+    } else
+	base = -base;
     for (x = v; x; digs++)
 	x /= base;
     if (!digs)
@@ -2974,7 +3259,10 @@ scanendscope(HashNode hn, int flags)
 	    pm->flags = (tpm->flags & ~PM_NORESTORE);
 	    pm->level = tpm->level;
 	    pm->ct = tpm->ct;
-	    pm->env = tpm->env;
+	    if (pm->env) {
+		delenv(pm->env);
+	    }
+	    pm->env = NULL;
 
 	    if (!(tpm->flags & PM_NORESTORE))
 		switch (PM_TYPE(pm->flags)) {
@@ -2996,6 +3284,9 @@ scanendscope(HashNode hn, int flags)
 		    break;
 		}
 	    zfree(tpm, sizeof(*tpm));
+
+	    if (pm->flags & PM_EXPORTED)
+		export_param(pm);
 	} else
 	    unsetparam_pm(pm, 0, 0);
     }
@@ -3067,7 +3358,8 @@ printparamnode(HashNode hn, int printflags)
 	    printf("exported ");
     }
 
-    if (printflags & PRINT_NAMEONLY) {
+    if ((printflags & PRINT_NAMEONLY) ||
+	((p->flags & PM_HIDEVAL) && !(printflags & PRINT_INCLUDEVALUE))) {
 	zputs(p->nam, stdout);
 	putchar('\n');
 	return;
diff --git a/zshconfig.ac b/zshconfig.ac
index b36174f4c..d6d76bc78 100644
--- a/zshconfig.ac
+++ b/zshconfig.ac
@@ -1304,7 +1304,7 @@ main()
     if(pid < 0)
 	exit(1);
     if(pid) {
-	fd = open("/tmp/fifo$$", O_RDWR);
+	fd = open("/tmp/fifo$$", O_RDONLY);
 	exit(fd < 0 || read(fd, &c, 1) != 1 || c != 'x');
     }
     fd = open("/tmp/fifo$$", O_WRONLY);