about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Config/version.mk4
-rw-r--r--Src/Modules/zutil.c10
-rw-r--r--Src/Zle/compcore.c2
-rw-r--r--Src/Zle/computil.c15
-rw-r--r--Src/Zle/zle_main.c6
-rw-r--r--Src/Zle/zle_refresh.c2
-rw-r--r--Src/builtin.c41
-rw-r--r--Src/glob.c2
-rw-r--r--Src/params.c153
-rw-r--r--Src/prompt.c2
-rw-r--r--Src/subst.c27
-rw-r--r--Src/utils.c6
-rw-r--r--Src/zsh.h3
13 files changed, 196 insertions, 77 deletions
diff --git a/Config/version.mk b/Config/version.mk
index 0ebed5e78..a15ccb964 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
 # This must also serve as a shell script, so do not add spaces around the
 # `=' signs.
 
-VERSION=5.3.1-dev-0
-VERSION_DATE='December 22, 2016'
+VERSION=5.3.1-dev-0-badarrays
+VERSION_DATE='May 28, 2017'
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 19a8306b5..82542cf4f 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -42,11 +42,11 @@ savematch(MatchData *m)
     char **a;
 
     queue_signals();
-    a = getaparam("match");
+    a = getaparam("match", NULL);
     m->match = a ? zarrdup(a) : NULL;
-    a = getaparam("mbegin");
+    a = getaparam("mbegin", NULL);
     m->mbegin = a ? zarrdup(a) : NULL;
-    a = getaparam("mend");
+    a = getaparam("mend", NULL);
     m->mend = a ? zarrdup(a) : NULL;
     unqueue_signals();
 }
@@ -402,7 +402,7 @@ evalstyle(Stypat p)
     errflag = ef | (errflag & ERRFLAG_INT);
 
     queue_signals();
-    if ((ret = getaparam("reply")))
+    if ((ret = getaparam("reply", NULL)))
 	ret = arrdup(ret);
     else if ((str = getsparam("reply"))) {
 	ret = (char **) hcalloc(2 * sizeof(char *));
@@ -1343,7 +1343,7 @@ rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp)
 		int len;
 
 		queue_signals();
-		mend = getaparam("mend");
+		mend = getaparam("mend", NULL);
 		len = atoi(mend[0]);
 		unqueue_signals();
 
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index d1cf7a08a..5fc15b0d3 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -1971,7 +1971,7 @@ get_user_var(char *nam)
 	char **arr = NULL, *val;
 
 	queue_signals();
-	if ((arr = getaparam(nam)) || (arr = gethparam(nam)))
+	if ((arr = getaparam(nam, NULL)) || (arr = gethparam(nam)))
 	    arr = (incompfunc ? arrdup(arr) : arr);
 	else if ((val = getsparam(nam))) {
 	    arr = (char **) zhalloc(2*sizeof(char *));
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index e704f9ffa..7ffe00df4 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -871,7 +871,7 @@ bin_compdescribe(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
         } else {
             char **opts;
 
-            if (!(opts = getaparam(args[4]))) {
+            if (!(opts = getaparam(args[4], NULL))) {
 		zwarnnam(nam, "unknown parameter: %s", args[4]);
 		return 1;
             }
@@ -4929,17 +4929,16 @@ bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		return 1;
 	    }
 	    queue_signals();
-	    if (!(tmp = getaparam(args[1]))) {
-		unqueue_signals();
+	    if (!(tmp = getaparam(args[1], NULL))) {
 		zwarnnam(nam, "unknown parameter: %s", args[1]);
 		return 0;
 	    }
 	    for (l = newlinklist(); *tmp; tmp++)
 		addlinknode(l, quotestring(*tmp, QT_BACKSLASH_PATTERN));
 	    set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2],
-					    l, getaparam(args[2]), args[3],
+					    l, getaparam(args[2], NULL), args[3],
 					    args[4], args[5],
-					    getaparam(args[6]), args + 7));
+					    getaparam(args[6], NULL), args + 7));
 	    unqueue_signals();
 	    return 0;
 	}
@@ -4960,12 +4959,12 @@ bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		return 1;
 	    }
 	    queue_signals();
-	    tmp = getaparam(args[2]);
+	    tmp = getaparam(args[2], NULL);
 	    l = newlinklist();
 	    if (tmp)
 		for (; *tmp; tmp++)
 		    addlinknode(l, *tmp);
-	    if (!(tmp = getaparam(args[1]))) {
+	    if (!(tmp = getaparam(args[1], NULL))) {
 		unqueue_signals();
 		zwarnnam(nam, "unknown parameter: %s", args[1]);
 		return 0;
@@ -4990,7 +4989,7 @@ bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		return 1;
 	    }
 	    queue_signals();
-	    if (!(tmp = getaparam(args[1]))) {
+	    if (!(tmp = getaparam(args[1], NULL))) {
 		unqueue_signals();
 		zwarnnam(nam, "unknown parameter: %s", args[1]);
 		return 0;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 6c271b5d0..dda2f56e6 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1193,6 +1193,7 @@ char *
 zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 {
     char *s, **bracket;
+    int bracket_len;
     int old_errno = errno;
     int tmout = getiparam("TMOUT");
 
@@ -1328,7 +1329,8 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
     if (zleline && *zleline)
 	redrawhook();
 
-    if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
+    if ((bracket = getaparam("zle_bracketed_paste", &bracket_len)) &&
+	bracket_len == 2)
 	fputs(*bracket, shout);
 
     zrefresh();
@@ -1340,7 +1342,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 		  "ZLE_VARED_ABORTED" :
 		  "ZLE_LINE_ABORTED", zlegetline(NULL, NULL));
 
-    if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
+    if ((bracket = getaparam("zle_bracketed_paste", &bracket_len)) && bracket_len == 2)
 	fputs(bracket[1], shout);
 
     if (done && !exit_pending && !errflag)
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index d0dd1ef06..a64e2f29e 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -313,7 +313,7 @@ static const REFRESH_ELEMENT zr_start_ellipsis[] = {
 static void
 zle_set_highlight(void)
 {
-    char **atrs = getaparam("zle_highlight");
+    char **atrs = getaparam("zle_highlight", NULL);
     int special_atr_on_set = 0;
     int region_atr_on_set = 0;
     int isearch_atr_on_set = 0;
diff --git a/Src/builtin.c b/Src/builtin.c
index 063644efb..2a2b22d3b 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -30,6 +30,8 @@
 /* this is defined so we get the prototype for open_memstream */
 #define _GNU_SOURCE 1
 
+#include <assert.h>
+
 #include "zsh.mdh"
 #include "builtin.pro"
 
@@ -53,7 +55,7 @@ static struct builtin builtins[] =
     BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
     BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
+    BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "ACE:%F:%HL:%R:%TUZ:%acfghi:%klmprtuxz", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -86,7 +88,7 @@ static struct builtin builtins[] =
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
-    BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
+    BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "ACE:%F:%HL:%R:%TUZ:%achi:%lprtux", NULL),
     BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
     BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
 
@@ -106,7 +108,7 @@ static struct builtin builtins[] =
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
     BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "IlLnr", NULL),
     BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL),
-    BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_READONLY, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
+    BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_READONLY, "ACE:%F:%HL:%R:%TUZ:%acfghi:%lptux", "r"),
     BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"),
     BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
     BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL),
@@ -120,7 +122,7 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
     BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"),
-    BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
+    BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "ACE:%F:%HL:%R:%TUZ:%acfghi:%klprtuxmz", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
@@ -673,11 +675,9 @@ bin_set(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
     if (array) {
 	/* create an array with the specified elements */
 	char **a = NULL, **y;
-	int len = arrlen(args);
-
-	if (array < 0 && (a = getaparam(arrayname))) {
-	    int al = arrlen(a);
+	int len = arrlen(args), al;
 
+	if (array < 0 && (a = getaparam(arrayname, &al))) {
 	    if (al > len)
 		len = al;
 	}
@@ -2579,6 +2579,17 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	else if (OPT_PLUS(ops,optval))
 	    off |= bit;
     }
+
+    /* Special case, ran out of bit<<1 positions for optstr */
+    if (OPT_MINUS(ops,'c'))
+	on |= PM_CACHELEN;
+    else if (OPT_PLUS(ops,'c'))
+	off |= PM_CACHELEN;
+    if (OPT_MINUS(ops,'C'))
+	on |= PM_CHECKLEN;
+    else if (OPT_PLUS(ops,'C'))
+	off |= PM_CHECKLEN;
+
     roff = off;
 
     /* Sanity checks on the options.  Remove conflicting options. */
@@ -3611,7 +3622,10 @@ bin_unset(char *name, char **argv, Options ops, int func)
 		    } else {
 			/* start is after the element for reverse index */
 			int start = vbuf.start - !!(vbuf.flags & VALFLAG_INV);
-			if (arrlen_gt(vbuf.pm->u.arr, start)) {
+			if (!(vbuf.pm->node.flags & PM_CACHELEN))
+			    vbuf.pm->length = arrlen(vbuf.pm->u.arr);
+			assert(vbuf.pm->length == arrlen(vbuf.pm->u.arr));
+			if (start < vbuf.pm->length) {
 			    char *arr[2];
 			    arr[0] = "";
 			    arr[1] = 0;
@@ -5264,7 +5278,7 @@ bin_shift(char *name, char **argv, Options ops, UNUSED(int func))
 
     /* optional argument can be either numeric or an array */
     queue_signals();
-    if (*argv && !getaparam(*argv))
+    if (*argv && !getaparam(*argv, NULL))
         num = mathevali(*argv++);
 
     if (num < 0) {
@@ -5274,9 +5288,10 @@ bin_shift(char *name, char **argv, Options ops, UNUSED(int func))
     }
 
     if (*argv) {
+	int len = 0;
         for (; *argv; argv++)
-            if ((s = getaparam(*argv))) {
-                if (arrlen_lt(s, num)) {
+            if ((s = getaparam(*argv, &len))) {
+                if (num > len) {
 		    zwarnnam(name, "shift count must be <= $#");
 		    ret++;
 		    continue;
@@ -5284,7 +5299,7 @@ bin_shift(char *name, char **argv, Options ops, UNUSED(int func))
 		if (OPT_ISSET(ops,'p')) {
 		    char **s2, **src, **dst;
 		    int count;
-		    l = arrlen(s);
+		    l = len;
 		    src = s;
 		    dst = s2 = (char **)zalloc((l - num + 1) * sizeof(char *));
 		    for (count = l - num; count; count--)
diff --git a/Src/glob.c b/Src/glob.c
index af5d0821c..aeb5fd204 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -3860,7 +3860,7 @@ qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str)
 	errflag = ef | (errflag & ERRFLAG_INT);
 	lastval = lv;
 
-	if (!(inserts = getaparam("reply")) &&
+	if (!(inserts = getaparam("reply", NULL)) &&
 	    !(inserts = gethparam("reply"))) {
 	    char *tmp;
 
diff --git a/Src/params.c b/Src/params.c
index d92dd228a..75eff1ebb 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -27,6 +27,8 @@
  *
  */
 
+#include <assert.h>
+
 #include "zsh.mdh"
 #include "params.pro"
 
@@ -264,6 +266,7 @@ typedef struct iparam {
     void *gsu;			/* get/set/unset methods */
     int base;			/* output base                           */
     int width;			/* output field width                    */
+    int length;                 /* length of array                       */
     char *env;			/* location in environment, if exported  */
     char *ename;		/* name of corresponding environment var */
     Param old;			/* old struct for use with local         */
@@ -274,7 +277,7 @@ typedef struct iparam {
 static initparam special_params[] ={
 #define GSU(X) BR((GsuScalar)(void *)(&(X)))
 #define NULL_GSU BR((GsuScalar)(void *)NULL)
-#define IPDEF1(A,B,C) {{NULL,A,PM_INTEGER|PM_SPECIAL|C},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
+#define IPDEF1(A,B,C) {{NULL,A,PM_INTEGER|PM_SPECIAL|C},BR(NULL),GSU(B),10,0,0,NULL,NULL,NULL,0}
 IPDEF1("#", pound_gsu, PM_READONLY),
 IPDEF1("ERRNO", errno_gsu, PM_UNSET),
 IPDEF1("GID", gid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
@@ -287,7 +290,7 @@ IPDEF1("UID", uid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("EUID", euid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY),
 
-#define IPDEF2(A,B,C) {{NULL,A,PM_SCALAR|PM_SPECIAL|C},BR(NULL),GSU(B),0,0,NULL,NULL,NULL,0}
+#define IPDEF2(A,B,C) {{NULL,A,PM_SCALAR|PM_SPECIAL|C},BR(NULL),GSU(B),0,0,0,NULL,NULL,NULL,0}
 IPDEF2("USERNAME", username_gsu, PM_DONTIMPORT|PM_RESTRICTED),
 IPDEF2("-", dash_gsu, PM_READONLY),
 IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
@@ -322,7 +325,7 @@ LCIPDEF("LC_TIME"),
 # endif
 #endif /* USE_LOCALE */
 
-#define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,NULL,NULL,NULL,0}
+#define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,0,NULL,NULL,NULL,0}
 IPDEF4("!", &lastpid),
 IPDEF4("$", &mypid),
 IPDEF4("?", &lastval),
@@ -331,22 +334,22 @@ IPDEF4("LINENO", &lineno),
 IPDEF4("PPID", &ppid),
 IPDEF4("ZSH_SUBSHELL", &zsh_subshell),
 
-#define IPDEF5(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
-#define IPDEF5U(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
+#define IPDEF5(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL},BR((void *)B),GSU(F),10,0,0,NULL,NULL,NULL,0}
+#define IPDEF5U(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(F),10,0,0,NULL,NULL,NULL,0}
 IPDEF5("COLUMNS", &zterm_columns, zlevar_gsu),
 IPDEF5("LINES", &zterm_lines, zlevar_gsu),
 IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, rprompt_indent_gsu),
 IPDEF5("SHLVL", &shlvl, varinteger_gsu),
 
 /* Don't import internal integer status variables. */
-#define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
+#define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,0,NULL,NULL,NULL,0}
 IPDEF6("OPTIND", &zoptind, varinteger_gsu),
 IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
 IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
 
-#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
-#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_SUID},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
-#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
+#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,0,NULL,NULL,NULL,0}
+#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_SUID},BR((void *)B),GSU(varscalar_gsu),0,0,0,NULL,NULL,NULL,0}
+#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,0,NULL,NULL,NULL,0}
 IPDEF7("OPTARG", &zoptarg),
 IPDEF7("NULLCMD", &nullcmd),
 IPDEF7U("POSTEDIT", &postedit),
@@ -361,7 +364,7 @@ IPDEF7("PS3", &prompt3),
 IPDEF7R("PS4", &prompt4),
 IPDEF7("SPROMPT", &sprompt),
 
-#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0}
+#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,0,NULL,C,NULL,0}
 #define IPDEF9(A,B,C) IPDEF9F(A,B,C,0)
 IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
 IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
@@ -370,9 +373,9 @@ IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
  * This empty row indicates the end of parameters available in
  * all emulations.
  */
-{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
+{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,0,NULL,NULL,NULL,0},
 
-#define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0}
+#define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,0,NULL,C,NULL,0}
 IPDEF8("CDPATH", &cdpath, "cdpath", 0),
 IPDEF8("FIGNORE", &fignore, "fignore", 0),
 IPDEF8("FPATH", &fpath, "fpath", 0),
@@ -385,7 +388,7 @@ IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY),
 /* MODULE_PATH is not imported for security reasons */
 IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED),
 
-#define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
+#define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,0,NULL,NULL,NULL,0}
 
 /*
  * The following parameters are not available in sh/ksh compatibility *
@@ -420,7 +423,7 @@ IPDEF9F("path", &path, "PATH", PM_RESTRICTED),
 
 IPDEF10("pipestatus", pipestatus_gsu),
 
-{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
+{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,0,NULL,NULL,NULL,0},
 };
 
 /*
@@ -440,7 +443,7 @@ IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY),
 /* MODULE_PATH is not imported for security reasons */
 IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED),
 
-{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
+{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,0,NULL,NULL,NULL,0},
 };
 
 /*
@@ -744,6 +747,20 @@ static int dontimport(int flags)
     return 0;
 }
 
+/**/
+int
+arrcachelen(Param pm)
+{
+    int len;
+
+    len = pm->length;
+    if (len == 0 && pm->u.arr) {
+       len = arrlen(pm->u.arr);
+       pm->length = len;
+    }
+    return len;
+}
+
 /* Set up parameter hash table.  This will add predefined  *
  * parameter entries as well as setting up parameter table *
  * entries for environment variables we inherit.           */
@@ -991,7 +1008,7 @@ createparam(char *name, int flags)
 	    }
 
 	    pm = oldpm;
-	    pm->base = pm->width = 0;
+	    pm->base = pm->width = pm->length = 0;
 	    oldpm = pm->old;
 	} else {
 	    pm = (Param) zshcalloc(sizeof *pm);
@@ -1104,6 +1121,7 @@ copyparam(Param tpm, Param pm, int fakecopy)
      */
     tpm->node.flags = pm->node.flags;
     tpm->base = pm->base;
+    tpm->length = pm->length;
     tpm->width = pm->width;
     tpm->level = pm->level;
     if (!fakecopy)
@@ -1552,7 +1570,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
 		ta = getarrvalue(v);
 	    if (!ta || !*ta)
 		return !down;
-	    len = arrlen(ta);
+	    if (v->pm->node.flags & PM_CACHELEN) {
+		len = arrcachelen(v->pm);
+		if (v->pm->node.flags & PM_CHECKLEN)
+		    assert(len == arrlen(ta));
+	    } else
+		len = arrlen(ta);
 	    if (beg < 0)
 		beg += len;
 	    if (down) {
@@ -1575,7 +1598,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
 	    }
 	} else if (word) {
 	    ta = sepsplit(d = s = getstrvalue(v), sep, 1, 1);
-	    len = arrlen(ta);
+	    if (v->pm->node.flags & PM_CACHELEN) {
+		len = arrcachelen(v->pm);
+		if (v->pm->node.flags & PM_CHECKLEN)
+		    assert(len == arrlen(ta));
+	    } else
+		len = arrlen(ta);
 	    if (beg < 0)
 		beg += len;
 	    if (down) {
@@ -2140,10 +2168,20 @@ getstrvalue(Value v)
 	if (v->isarr)
 	    s = sepjoin(ss, NULL, 1);
 	else {
-	    if (v->start < 0)
-		v->start += arrlen(ss);
-	    s = (arrlen_le(ss, v->start) || v->start < 0) ?
-		(char *) hcalloc(1) : ss[v->start];
+	    if (v->pm->node.flags & PM_CACHELEN) {
+		len = arrcachelen(v->pm);
+		if (v->pm->node.flags & PM_CHECKLEN)
+		    assert(v->pm->length == arrlen(ss));
+		if (v->start < 0)
+		    v->start += len;
+		s = (v->start >= len || v->start < 0) ?
+		    (char *) hcalloc(1) : ss[v->start];
+	    } else {
+		if (v->start < 0)
+		    v->start += arrlen(ss);
+		s = (arrlen_le(ss, v->start) || v->start < 0) ?
+		    (char *) hcalloc(1) : ss[v->start];
+	    }
 	}
 	return s;
     case PM_INTEGER:
@@ -2341,10 +2379,20 @@ getarrvalue(Value v)
     s = getvaluearr(v);
     if (v->start == 0 && v->end == -1)
 	return s;
-    if (v->start < 0)
-	v->start += arrlen(s);
-    if (v->end < 0)
-	v->end += arrlen(s) + 1;
+    if (v->pm->node.flags & PM_CACHELEN) {
+	int len = arrcachelen(v->pm);
+	if (v->pm->node.flags & PM_CHECKLEN)
+	    assert(v->pm->length == arrlen(s));
+	if (v->start < 0)
+	    v->start += len;
+	if (v->end < 0)
+	    v->end += len + 1;
+    } else {
+	if (v->start < 0)
+	    v->start += arrlen(s);
+	if (v->end < 0)
+	    v->end += arrlen(s) + 1;
+    }
 
     /* Null if 1) array too short, 2) index still negative */
     if (v->end <= v->start) {
@@ -2353,7 +2401,8 @@ getarrvalue(Value v)
     else if (v->start < 0) {
 	s = arrdup_max(nular, 1);
     }
-    else if (arrlen_le(s, v->start)) {
+    else if ((v->pm->node.flags & PM_CACHELEN) ?
+	     v->start > v->pm->length : arrlen_le(s, v->start)) {
 	/* Handle $ary[i,i] consistently for any $i > $#ary
 	 * and $ary[i,j] consistently for any $j > $i > $#ary
 	 */
@@ -2714,10 +2763,15 @@ setarrvalue(Value v, char **val)
 	char **const old = v->pm->gsu.a->getfn(v->pm);
 	char **new;
 	char **p, **q, **r; /* index variables */
-	const int pre_assignment_length = arrlen(old);
+	int pre_assignment_length = arrcachelen(v->pm);
 	int post_assignment_length;
 	int i;
 
+	if (v->pm->node.flags & PM_CACHELEN) {
+	    if (v->pm->node.flags & PM_CHECKLEN)
+		assert(v->pm->length == arrlen(old));
+	} else
+	    pre_assignment_length = arrlen(old);
 	q = old;
 
 	if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS)) {
@@ -2810,6 +2864,7 @@ setarrvalue(Value v, char **val)
 
                 v->pm->gsu.a->setfn(v->pm, new);
             }
+	    v->pm->length = post_assignment_length;
 
 	    DPUTS2(p - new != post_assignment_length, "setarrvalue: wrong allocation: %d 1= %lu",
 		   post_assignment_length, (unsigned long)(p - new));
@@ -2880,14 +2935,25 @@ getsparam_u(char *s)
 
 /**/
 mod_export char **
-getaparam(char *s)
+getaparam(char *s, int *len)
 {
     struct value vbuf;
     Value v;
 
     if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) &&
 	PM_TYPE(v->pm->node.flags) == PM_ARRAY)
+    {
+	if (len) {
+	    if (v->pm->node.flags & PM_CACHELEN) {
+		*len = arrcachelen(v->pm);
+		if (v->pm->node.flags & PM_CHECKLEN)
+		    assert (*len == arrlen(v->pm->gsu.a->getfn(v->pm)));
+	    } else
+		*len = arrlen(v->pm->gsu.a->getfn(v->pm));
+	}
+	//fprintf(stderr, "%i %i\n", v->pm->length, arrlen(v->pm->gsu.a->getfn(v->pm)));
 	return v->pm->gsu.a->getfn(v->pm);
+    }
     return NULL;
 }
 
@@ -3061,7 +3127,10 @@ assignsparam(char *s, char *val, int flags)
 		return v->pm; /* avoid later setstrvalue() call */
 	    case PM_ARRAY:
 	    	if (unset(KSHARRAYS)) {
-		    v->start = arrlen(v->pm->gsu.a->getfn(v->pm));
+		    if (v->pm->node.flags & PM_CACHELEN)
+			v->start = arrcachelen(v->pm);
+		    else
+			v->start = arrlen(v->pm->gsu.a->getfn(v->pm));
 		    v->end = v->start + 1;
 		} else {
 		    /* ksh appends scalar to first element */
@@ -3164,6 +3233,8 @@ assignaparam(char *s, char **val, int flags)
 		char **new;
 		int lv = arrlen(val);
 
+		v->pm->length = lv + 1;
+
 		new = (char **) zalloc(sizeof(char *) * (lv + 2));
 		*new = ztrdup(getstrvalue(v));
 		memcpy(new+1, val, sizeof(char *) * (lv + 1));
@@ -3188,7 +3259,16 @@ assignaparam(char *s, char **val, int flags)
     if (flags & ASSPM_AUGMENT) {
     	if (v->start == 0 && v->end == -1) {
 	    if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
-	    	v->start = arrlen(v->pm->gsu.a->getfn(v->pm));
+		if (v->pm->node.flags & PM_CACHELEN) {
+		    v->start = 
+			//arrlen(v->pm->gsu.a->getfn(v->pm));
+			arrcachelen(v->pm);
+		    if (v->pm->node.flags & PM_CHECKLEN)
+			assert(v->pm->length == arrlen(v->pm->gsu.a->getfn(v->pm)));
+		} else {
+		    v->start = 
+			arrlen(v->pm->gsu.a->getfn(v->pm));
+		}
 	    	v->end = v->start + 1;
 	    } else if (PM_TYPE(v->pm->node.flags) & PM_HASHED)
 	    	v->start = -1, v->end = 0;
@@ -3196,7 +3276,16 @@ assignaparam(char *s, char **val, int flags)
 	    if (v->end > 0)
 		v->start = v->end--;
 	    else if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
-		v->end = arrlen(v->pm->gsu.a->getfn(v->pm)) + v->end;
+		if (v->pm->node.flags & PM_CACHELEN) {
+		    v->end 
+			//= arrlen(v->pm->gsu.a->getfn(v->pm)) + v->end;
+			+= arrcachelen(v->pm);
+		    if (v->pm->node.flags & PM_CHECKLEN)
+			assert(v->pm->length == arrlen(v->pm->gsu.a->getfn(v->pm)));
+		} else {
+		    v->end 
+			= arrlen(v->pm->gsu.a->getfn(v->pm)) + v->end;
+		}
 		v->start = v->end + 1;
 	    }
 	}
diff --git a/Src/prompt.c b/Src/prompt.c
index c478e69fb..a8bb10329 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -1889,7 +1889,7 @@ allocate_colour_buffer(void)
     if (colseq_buf_allocs++)
 	return;
 
-    atrs = getaparam("zle_highlight");
+    atrs = getaparam("zle_highlight", NULL);
     if (atrs) {
 	for (; *atrs; atrs++) {
 	    if (strpfx("fg_start_code:", *atrs)) {
diff --git a/Src/subst.c b/Src/subst.c
index 5b1bf8988..a73ac4737 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -27,6 +27,8 @@
  *
  */
 
+#include <assert.h>
+
 #include "zsh.mdh"
 #include "subst.pro"
 
@@ -35,6 +37,8 @@
 /**/
 char nulstring[] = {Nularg, '\0'};
 
+int arrcachelen(Param pm);
+    
 /* Do substitutions before fork. These are:
  *  - Process substitution: <(...), >(...), =(...)
  *  - Parameter substitution
@@ -2555,9 +2559,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		 * necessary joining of arrays until this point
 		 * to avoid the multsub() horror.
 		 */
-
-		/* arrlen() is expensive, so only compute it if needed. */
-		int tmplen = -1;
+		int tmplen;
+		if (v->pm->node.flags & PM_CACHELEN) {
+		    tmplen = arrcachelen(v->pm);
+		    if (v->pm->node.flags & PM_CHECKLEN)
+			assert(tmplen == arrlen(v->pm->gsu.a->getfn(v->pm)));
+		} else
+		    tmplen = -1;
 
 		if (v->start < 0) {
 		    tmplen = arrlen(v->pm->gsu.a->getfn(v->pm));
@@ -3068,11 +3076,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    val = dupstring("");
 	} else {
 	    char *sval;
-	    zip = getaparam(s);
+	    int ziplen;
+	    zip = getaparam(s, &ziplen);
 	    if (!zip) {
 		sval = getsparam(s);
 		if (sval)
 		    zip = hmkarray(sval);
+		ziplen = 1;
+		ziplen = !!sval;
 	    }
 	    if (!isarr) {
 		aval = mkarray(val);
@@ -3080,9 +3091,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    }
 	    if (zip) {
 		char **out;
-		int alen, ziplen, outlen, i = 0;
+		int alen, outlen, i = 0;
 		alen = arrlen(aval);
-		ziplen = arrlen(zip);
 		outlen = shortest ^ (alen > ziplen) ? alen : ziplen;
 		if (!shortest && (alen == 0 || ziplen == 0)) {
 		    if (ziplen)
@@ -3112,6 +3122,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
     } else if (inbrace && (*s == '|' || *s == Bar ||
 			   *s == '*' || *s == Star)) {
 	int intersect = (*s == '*' || *s == Star);
+	int compare_len;
 	char **compare, **ap, **apsrc;
 	++s;
 	if (*itype_end(s, IIDENT, 0)) {
@@ -3119,9 +3130,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    zerr("not an identifier: %s", s);
 	    return NULL;
 	}
-	compare = getaparam(s);
+	compare = getaparam(s, &compare_len);
 	if (compare) {
-	    HashTable ht = newuniqtable(arrlen(compare)+1);
+	    HashTable ht = newuniqtable(compare_len+1);
 	    int present;
 	    for (ap = compare; *ap; ap++)
 		(void)addhashnode2(ht, *ap, (HashNode)
diff --git a/Src/utils.c b/Src/utils.c
index acb891d82..579ab3be8 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -1501,7 +1501,7 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
 	memcpy(arrnam, name, namlen);
 	memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN);
 
-	if ((arrptr = getaparam(arrnam))) {
+	if ((arrptr = getaparam(arrnam, NULL))) {
 	    arrptr = arrdup(arrptr);
 	    for (; *arrptr; arrptr++) {
 		if ((shfunc = getshfunc(*arrptr))) {
@@ -3810,7 +3810,7 @@ subst_string_by_func(Shfunc func, char *arg1, char *orig)
     if (doshfunc(func, l, 1))
 	ret = NULL;
     else
-	ret = getaparam("reply");
+	ret = getaparam("reply", NULL);
 
     sfcontext = osc;
     stopmsg = osm;
@@ -3842,7 +3842,7 @@ subst_string_by_hook(char *name, char *arg1, char *orig)
 	memcpy(arrnam, name, namlen);
 	memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN);
 
-	if ((arrptr = getaparam(arrnam))) {
+	if ((arrptr = getaparam(arrnam, NULL))) {
 	    /* Guard against internal modification of the array */
 	    arrptr = arrdup(arrptr);
 	    for (; *arrptr; arrptr++) {
diff --git a/Src/zsh.h b/Src/zsh.h
index 22f73f806..5fbd95469 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1779,6 +1779,7 @@ struct param {
 
     int base;			/* output base or floating point prec    */
     int width;			/* field width                           */
+    int length;                 /* length of array                       */
     char *env;			/* location in environment, if exported  */
     char *ename;		/* name of corresponding environment var */
     Param old;			/* old struct for use with local         */
@@ -1833,6 +1834,8 @@ struct tieddata {
 
 #define PM_KSHSTORED	(1<<17) /* function stored in ksh form              */
 #define PM_ZSHSTORED	(1<<18) /* function stored in zsh form              */
+#define PM_CACHELEN     (1<<17) /* length is cached */
+#define PM_CHECKLEN     (1<<18) /* cached length is checked */
 
 /* Remaining flags do not correspond directly to command line arguments */
 #define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */