about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-05-31 17:10:12 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-05-31 17:10:12 +0000
commitf42e3fa8e6152e145251e8f16f4c61c23dec1f59 (patch)
tree9a7b104b910ae53f6a49cb4e35c3dedc737e907b /Src
parent4fec788fa5f6e7c9723e02e3d0b57068ce9785aa (diff)
downloadzsh-f42e3fa8e6152e145251e8f16f4c61c23dec1f59.tar.gz
zsh-f42e3fa8e6152e145251e8f16f4c61c23dec1f59.tar.xz
zsh-f42e3fa8e6152e145251e8f16f4c61c23dec1f59.zip
Diffstat (limited to 'Src')
-rw-r--r--Src/Makefile.in15
-rw-r--r--Src/Makemod.in.in13
-rw-r--r--Src/Modules/mapfile.c369
-rw-r--r--Src/Modules/mapfile.mdd3
-rw-r--r--Src/Modules/parameter.c25
-rw-r--r--Src/Zle/compctl.c1
-rw-r--r--Src/Zle/zle_tricky.c31
-rw-r--r--Src/builtin.c3
-rw-r--r--Src/exec.c2
-rw-r--r--Src/init.c21
-rw-r--r--Src/options.c4
-rw-r--r--Src/params.c36
-rw-r--r--Src/subst.c8
-rw-r--r--Src/zsh.export2
-rw-r--r--Src/zsh.h2
15 files changed, 474 insertions, 61 deletions
diff --git a/Src/Makefile.in b/Src/Makefile.in
index 0babdf47b..dd96aaf90 100644
--- a/Src/Makefile.in
+++ b/Src/Makefile.in
@@ -28,8 +28,15 @@ subdir = Src
 dir_top = ..
 SUBDIRS =
 
-@@version.mk@@
-@@defs.mk@@
+@VERSION_MK@
+
+# source/build directories
+VPATH           = @srcdir@
+sdir            = @srcdir@
+sdir_top        = @top_srcdir@
+INSTALL         = @INSTALL@
+
+@DEFS_MK@
 
 sdir_src      = $(sdir)
 dir_src       = .
@@ -85,7 +92,7 @@ modobjs: headers rm-modobjs-tmp
 rm-modobjs-tmp:
 	rm -f stamp-modobjs.tmp
 
-@@config.mk@@
+@CONFIG_MK@
 
 Makemod modules.index prep: modules-bltin $(CONFIG_INCS)
 	( cd $(sdir_top) && $(SHELL) $(subdir)/mkmodindex.sh $(subdir) ) \
@@ -201,7 +208,7 @@ realclean: realclean-modules
 mostlyclean-modules clean-modules distclean-modules realclean-modules: Makemod
 	@$(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`
 
-@@clean.mk@@
+@CLEAN_MK@
 
 # ========== RECURSIVE MAKES ==========
 
diff --git a/Src/Makemod.in.in b/Src/Makemod.in.in
index a5c760eb4..42cfc0401 100644
--- a/Src/Makemod.in.in
+++ b/Src/Makemod.in.in
@@ -30,8 +30,15 @@
 # dir_top is done by mkmakemod.sh
 # SUBDIRS is done by mkmakemod.sh
 
-@@version.mk@@
-@@defs.mk@@
+@VERSION_MK@
+
+# source/build directories
+VPATH           = @srcdir@
+sdir            = @srcdir@
+sdir_top        = @top_srcdir@
+INSTALL         = @INSTALL@
+
+@DEFS_MK@
 
 sdir_src      = $(sdir_top)/Src
 dir_src       = $(dir_top)/Src
@@ -134,7 +141,7 @@ uninstall.modules-here:
 
 # ========== DEPENDENCIES FOR CLEANUP ==========
 
-@@clean.mk@@
+@CLEAN_MK@
 
 mostlyclean-here:
 	rm -f *.o *.$(DL_EXT)
diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c
new file mode 100644
index 000000000..e98da7889
--- /dev/null
+++ b/Src/Modules/mapfile.c
@@ -0,0 +1,369 @@
+/*
+ * mapfile.c - associative array interface to external files
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky, Peter Stephenson
+ * 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 Sven Wischnowsky, Peter Stephenson 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 Peter Stephenson, Sven Wischnowsky and the Zsh
+ * Development Group have been advised of the possibility of such damage.
+ *
+ * Peter Stephenson, Sven Wischnowsky 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 softwareprovided hereunder is on an "as is" basis, and Peter
+ * Stephenson, Sven Wischnowsky and the Zsh Development Group have no
+ * obligation to provide maintenance, support, updates, enhancements, or
+ * modifications. 
+ *
+ */
+
+/*
+ * To do:  worry about when keys of associative arrays get unmeta'd.
+ */
+#include "mapfile.mdh"
+#include "mapfile.pro"
+
+/*
+ * Make sure we have all the bits I'm using for memory mapping, otherwise
+ * I don't know what I'm doing.
+ */
+#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_FTRUNCATE)
+#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) && defined(HAVE_MSYNC)
+#define USE_MMAP 1
+
+#include <sys/mman.h>
+
+#if !defined(MAP_VARIABLE)
+#define MAP_VARIABLE 0
+#endif
+#if !defined(MAP_FILE)
+#define MAP_FILE 0
+#endif
+#if !defined(MAP_NORESERVE)
+#define MAP_NORESERVE 0
+#endif
+#define MMAP_ARGS (MAP_FILE | MAP_VARIABLE | MAP_SHARED | MAP_NORESERVE)
+
+#endif /* HAVE_MMAP && HAVE_MUNMAP && HAVE_MSYNC */
+#endif /* HAVE_SYS_MMAN_H &&  HAVE_FTRUNCATE */
+
+/*
+ * Name of the special parameter.  If zmodload took arguments,
+ * we could make this selectable.
+ */
+static char mapfile_nam[] = "mapfile";
+
+static Param mapfile_pm;
+
+/* Empty dummy function for special hash parameters. */
+
+/**/
+static void
+shempty(void)
+{
+}
+
+/* Create the special hash parameter. */
+
+/**/
+static Param
+createmapfilehash()
+{
+    Param pm;
+    HashTable ht;
+
+    unsetparam(mapfile_nam);
+    mapfile_pm = NULL;
+
+    if (!(pm = createparam(mapfile_nam, PM_SPECIAL|PM_REMOVABLE|PM_HASHED)))
+	return NULL;
+
+    pm->level = pm->old ? locallevel : 0;
+    pm->gets.hfn = hashgetfn;
+    pm->sets.hfn = setpmmapfiles;
+    pm->unsetfn = stdunsetfn;
+    pm->u.hash = ht = newhashtable(7, mapfile_nam, NULL);
+
+    ht->hash        = hasher;
+    ht->emptytable  = (TableFunc) shempty;
+    ht->filltable   = NULL;
+    ht->addnode     = (AddNodeFunc) shempty;
+    ht->getnode     = ht->getnode2 = getpmmapfile;
+    ht->removenode  = (RemoveNodeFunc) shempty;
+    ht->disablenode = NULL;
+    ht->enablenode  = NULL;
+    ht->freenode    = (FreeNodeFunc) shempty;
+    ht->printnode   = printparamnode;
+    ht->scantab     = scanpmmapfile;
+
+    return (mapfile_pm = pm);
+}
+
+/* Functions for the options special parameter. */
+
+/**/
+static void
+setpmmapfile(Param pm, char *value)
+{
+    int fd = -1, len;
+    char *name = ztrdup(pm->nam);
+#ifdef USE_MMAP
+    caddr_t mmptr;
+#else
+    FILE *fout;
+#endif
+
+    /*
+     * First unmetafy the value, and the name since we don't
+     * where it's been.
+     */
+    unmetafy(name, &len);
+    unmetafy(value, &len);
+
+    /* Open the file for writing */
+#ifdef USE_MMAP
+    if (!(pm->flags & PM_READONLY) &&
+	(fd = open(name, O_RDWR|O_CREAT|O_NOCTTY, 0666)) >= 0 &&
+	(mmptr = (caddr_t)mmap((caddr_t)0, len, PROT_READ | PROT_WRITE,
+			       MMAP_ARGS, fd, (off_t)0)) != (caddr_t)-1) {
+	/*
+	 * First we need to make sure the file is long enough for
+	 * when we msync.  On AIX, at least, we just get zeroes otherwise.
+	 */
+	ftruncate(fd, len);
+	memcpy(mmptr, value, len);
+	msync(mmptr, len, MS_SYNC);
+	/*
+	 * Then we need to truncate again, since mmap() always maps complete
+	 * pages.  Honestly, I tried it without, and you need both.
+	 */
+	ftruncate(fd, len);
+    }
+#else /* don't USE_MMAP */
+    /* can't be bothered to do anything too clever here */
+    if ((fout = fopen(name, "w"))) {
+	while (len--)
+	    putc(*value++, fout);
+	fclose(fout);
+    }
+#endif /* USE_MMAP */
+    if (fd >= 0)
+	close(fd);
+    free(name);
+    free(value);
+}
+
+/**/
+static void
+unsetpmmapfile(Param pm, int exp)
+{
+    /* Unlink the file given by pm->nam */
+    char *fname = ztrdup(pm->nam);
+    int dummy;
+    unmetafy(fname, &dummy);
+
+    if (!(pm->flags & PM_READONLY))
+	unlink(fname);
+
+    free(fname);
+}
+
+/**/
+static void
+setpmmapfiles(Param pm, HashTable ht)
+{
+    int i;
+    HashNode hn;
+
+    /* just to see if I've understood what's happening */
+    DPUTS(pm != mapfile_pm, "BUG: setpmmapfiles called for wrong param");
+
+    if (!ht)
+	return;
+
+    if (!(pm->flags & PM_READONLY))
+	for (i = 0; i < ht->hsize; i++)
+	    for (hn = ht->nodes[i]; hn; hn = hn->next) {
+		struct value v;
+
+		v.isarr = v.inv = v.a = 0;
+		v.b = -1;
+		v.arr = NULL;
+		v.pm = (Param) hn;
+
+		setpmmapfile(v.pm, ztrdup(getstrvalue(&v)));
+	    }
+    deleteparamtable(ht);
+}
+
+/**/
+static char *
+get_contents(char *fname)
+{
+    int fd;
+#ifdef USE_MMAP
+    caddr_t mmptr;
+    struct stat sbuf;
+#endif
+    char *val;
+    unmetafy(fname = ztrdup(fname), &fd);
+
+#ifdef USE_MMAP
+    if ((fd = open(fname, O_RDONLY | O_NOCTTY)) < 0 ||
+	fstat(fd, &sbuf) ||
+	(mmptr = (caddr_t)mmap((caddr_t)0, sbuf.st_size, PROT_READ,
+			       MMAP_ARGS, fd, (off_t)0)) == (caddr_t)-1) {
+	if (fd >= 0)
+	    close(fd);
+	free(fname);
+	return NULL;
+    }
+
+    /*
+     * Sadly, we need to copy the thing even if metafying doesn't
+     * change it.  We just don't know when we might get a chance to
+     * munmap it, otherwise.
+     */
+    val = metafy((char *)mmptr, sbuf.st_size, META_HEAPDUP);
+
+    munmap(mmptr, sbuf.st_size);
+    close(fd);
+#else /* don't USE_MMAP */
+    val = NULL;
+    if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) {
+	LinkList ll;
+	MUSTUSEHEAP("mapfile:get_contents");
+	if ((ll = readoutput(fd, 1)))
+	    val = peekfirst(ll);
+    }
+#endif /* USE_MMAP */
+    free(fname);
+    return val;
+}
+
+/**/
+static HashNode
+getpmmapfile(HashTable ht, char *name)
+{
+    char *contents;
+    Param pm = NULL;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR;
+	pm->sets.cfn = setpmmapfile;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = unsetpmmapfile;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+
+	pm->flags |= (mapfile_pm->flags & PM_READONLY);
+
+	/* Set u.str to contents of file given by name */
+	if ((contents = get_contents(pm->nam)))
+	    pm->u.str = contents;
+	else {
+	    pm->u.str = "";
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmmapfile(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    DIR *dir;
+
+    if (!(dir = opendir(".")))
+	return;
+
+    pm.flags = PM_SCALAR;
+    pm.sets.cfn = setpmmapfile;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = unsetpmmapfile;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    pm.flags |= (mapfile_pm->flags & PM_READONLY);
+
+    /* Here we scan the current directory, calling func() for each file */
+    while ((pm.nam = zreaddir(dir, 1))) {
+	/*
+	 * Hmmm, it's rather wasteful always to read the contents.
+	 * In fact, it's grotesequely wasteful, since that would mean
+	 * we always read the entire contents of every single file
+	 * in the directory into memory.  Hence just leave it empty.
+	 */
+	pm.u.str = "";
+	func((HashNode) &pm, flags);
+    }
+    closedir(dir);
+}
+
+/**/
+int
+setup_mapfile(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+boot_mapfile(Module m)
+{
+    /* Create the special associative array. */
+
+    if (!createmapfilehash())
+	return 1;
+
+    return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_mapfile(Module m)
+{
+    Param pm;
+
+    /* Remove the special parameter if it is still the same. */
+
+    if ((pm = (Param) paramtab->getnode(paramtab, mapfile_nam)) &&
+	pm == mapfile_pm) {
+	pm->flags &= ~PM_READONLY;
+	unsetparam_pm(pm, 0, 1);
+    }
+    return 0;
+}
+
+/**/
+int
+finish_mapfile(Module m)
+{
+    return 0;
+}
+
+#endif
diff --git a/Src/Modules/mapfile.mdd b/Src/Modules/mapfile.mdd
new file mode 100644
index 000000000..9adb36162
--- /dev/null
+++ b/Src/Modules/mapfile.mdd
@@ -0,0 +1,3 @@
+autoparams="mapfile"
+
+objects="mapfile.o"
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 2257933f5..4cb7b0aba 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -184,7 +184,7 @@ setpmcommand(Param pm, char *value)
 	Cmdnam cn = zcalloc(sizeof(*cn));
 
 	cn->flags = HASHED;
-	cn->u.cmd = ztrdup(value);
+	cn->u.cmd = value;
 
 	cmdnamtab->addnode(cmdnamtab, ztrdup(pm->nam), (HashNode) cn);
     }
@@ -207,6 +207,9 @@ setpmcommands(Param pm, HashTable ht)
     int i;
     HashNode hn;
 
+    if (!ht)
+	return;
+
     for (i = 0; i < ht->hsize; i++)
 	for (hn = ht->nodes[i]; hn; hn = hn->next) {
 	    Cmdnam cn = zcalloc(sizeof(*cn));
@@ -222,6 +225,7 @@ setpmcommands(Param pm, HashTable ht)
 
 	    cmdnamtab->addnode(cmdnamtab, ztrdup(hn->nam), (HashNode) cn);
 	}
+    deleteparamtable(ht);
 }
 
 /**/
@@ -312,14 +316,13 @@ scanpmcommands(HashTable ht, ScanFunc func, int flags)
 
 /**/
 static void
-setfunction(char *name, char *value)
+setfunction(char *name, char *val)
 {
-    char *val;
+    char *value = dupstring(val);
     Shfunc shf;
     List list;
     int sn;
 
-    val = ztrdup(value);
     val = metafy(val, strlen(val), META_REALLOC);
 
     HEAPALLOC {
@@ -327,7 +330,7 @@ setfunction(char *name, char *value)
     } LASTALLOC;
 
     if (!list || list == &dummy_list) {
-	zwarnnam(NULL, "invalid function definition", val, 0);
+	zwarnnam(NULL, "invalid function definition", value, 0);
 	zsfree(val);
 	return;
     }
@@ -348,7 +351,6 @@ setfunction(char *name, char *value)
 	}
 	shfunctab->addnode(shfunctab, ztrdup(name), shf);
     } LASTALLOC;
-
     zsfree(val);
 }
 
@@ -376,6 +378,9 @@ setpmfunctions(Param pm, HashTable ht)
     int i;
     HashNode hn;
 
+    if (!ht)
+	return;
+
     for (i = 0; i < ht->hsize; i++)
 	for (hn = ht->nodes[i]; hn; hn = hn->next) {
 	    struct value v;
@@ -385,8 +390,9 @@ setpmfunctions(Param pm, HashTable ht)
 	    v.arr = NULL;
 	    v.pm = (Param) hn;
 
-	    setfunction(hn->nam, getstrvalue(&v));
+	    setfunction(hn->nam, ztrdup(getstrvalue(&v)));
 	}
+    deleteparamtable(ht);
 }
 
 /**/
@@ -483,6 +489,7 @@ setpmoption(Param pm, char *value)
 	zwarnnam(NULL, "no such option: %s", pm->nam, 0);
     else if (dosetopt(n, (value && strcmp(value, "off")), 0))
 	zwarnnam(NULL, "can't change option: %s", pm->nam, 0);
+    zsfree(value);
 }
 
 /**/
@@ -504,6 +511,9 @@ setpmoptions(Param pm, HashTable ht)
     int i;
     HashNode hn;
 
+    if (!ht)
+	return;
+
     for (i = 0; i < ht->hsize; i++)
 	for (hn = ht->nodes[i]; hn; hn = hn->next) {
 	    struct value v;
@@ -521,6 +531,7 @@ setpmoptions(Param pm, HashTable ht)
 			      (val && strcmp(val, "off")), 0))
 		zwarnnam(NULL, "can't change option: %s", hn->nam, 0);
 	}
+    deleteparamtable(ht);
 }
 
 /**/
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 217678c00..f644dfc38 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -2279,6 +2279,7 @@ set_compstate(Param pm, HashTable ht)
 
 		    break;
 		}
+    deleteparamtable(ht);
 }
 
 /**/
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index aadbaec6c..41ea12e77 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -751,6 +751,7 @@ check_param(char *s, int set)
 	    wb = cs - offs;
 	    we = wb + e - b;
 	    ispar = (br >= 2 ? 2 : 1);
+	    b[we-wb] = '\0';
 	    return b;
 	}
     }
@@ -2327,7 +2328,7 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 		    }
 		    ow = w;
 
-		    if (!ict)
+		    if (!llen && !alen)
 			lm = mp;
 		    else
 			lm = NULL;
@@ -3596,15 +3597,22 @@ addmatches(Cadata dat, char **argv)
 	    /* Get the contents of the completion variables if we have
 	     * to perform matching. */
 	    if (dat->aflags & CAF_MATCH) {
-		lipre = dupstring(compiprefix);
-		lisuf = dupstring(compisuffix);
+		if (dat->aflags & CAF_QUOTE) {
+		    lipre = dupstring(compiprefix);
+		    lisuf = dupstring(compisuffix);
+		} else {
+		    lipre = quotename(compiprefix, NULL);
+		    lisuf = quotename(compisuffix, NULL);
+		}
 		lpre = dupstring(compprefix);
 		lsuf = dupstring(compsuffix);
 		llpl = strlen(lpre);
 		llsl = strlen(lsuf);
 		/* Test if there is an existing -P prefix. */
 		if (dat->pre && *dat->pre) {
-		    pl = pfxlen(dat->pre, lpre);
+		    char *dp = rembslash(dat->pre);
+
+		    pl = pfxlen(dp, lpre);
 		    llpl -= pl;
 		    lpre += pl;
 		}
@@ -5324,10 +5332,11 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	int pl = 0;
 
 	if (*s) {
+	    char *dp = rembslash(cc->prefix);
 	    /* First find out how much of the prefix is already on the line. */
 	    sd = dupstring(s);
 	    untokenize(sd);
-	    pl = pfxlen(cc->prefix, sd);
+	    pl = pfxlen(dp, sd);
 	    s += pl;
 	    sd += pl;
 	    offs -= pl;
@@ -5335,7 +5344,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     }
     /* Does this compctl have a suffix (compctl -S)? */
     if (cc->suffix) {
-	char *sdup = dupstring(cc->suffix);
+	char *sdup = rembslash(cc->suffix);
 	int sl = strlen(sdup), suffixll;
 
 	/* Ignore trailing spaces. */
@@ -7112,9 +7121,13 @@ do_single(Cmatch m)
 		menuinsc++;
 		if (menuwe)
 		    menuend++;
-		if ((!menucmp || menuwe) && isset(AUTOREMOVESLASH)) {
-		    makesuffix(1);
-		    suffixlen['/'] = 1;
+		if (!menucmp || menuwe) {
+		    if (m->remf || m->rems)
+			makesuffixstr(m->remf, m->rems, 1);
+		    else if (isset(AUTOREMOVESLASH)) {
+			makesuffix(1);
+			suffixlen['/'] = 1;
+		    }
 		}
 	    }
 	}
diff --git a/Src/builtin.c b/Src/builtin.c
index a90d59cae..4c9b159dd 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2962,7 +2962,8 @@ zexit(int val, int from_signal)
 	    if (islogin && !subsh) {
 		sourcehome(".zlogout");
 #ifdef GLOBAL_ZLOGOUT
-		source(GLOBAL_ZLOGOUT);
+		if (isset(RCS) && isset(GLOBALRCS))
+		    source(GLOBAL_ZLOGOUT);
 #endif
 	    }
 	}
diff --git a/Src/exec.c b/Src/exec.c
index 7d15facfd..952dfbb78 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2342,7 +2342,7 @@ getoutput(char *cmd, int qt)
 /* read output of command substitution */
 
 /**/
-static LinkList
+LinkList
 readoutput(int in, int qt)
 {
     LinkList ret;
diff --git a/Src/init.c b/Src/init.c
index 86d5aa505..7f1b2153a 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -759,26 +759,11 @@ run_init_scripts(void)
 	source(GLOBAL_ZSHENV);
 #endif
 	if (isset(RCS)) {
-	    int globalfirst = isset(GLOBALRCSFIRST);
-	    if (globalfirst) {
-#ifdef GLOBAL_ZPROFILE
-		if (islogin)
-		    source(GLOBAL_ZPROFILE);
-#endif
-#ifdef GLOBAL_ZSHRC
-		if (interact)
-		    source(GLOBAL_ZSHRC);
-#endif
-#ifdef GLOBAL_ZLOGIN
-		if (islogin)
-		    source(GLOBAL_ZLOGIN);
-#endif
-	    }
 	    if (unset(PRIVILEGED))
 		sourcehome(".zshenv");
 	    if (islogin) {
 #ifdef GLOBAL_ZPROFILE
-		if (!globalfirst)
+		if (isset(GLOBALRCS))
 		    source(GLOBAL_ZPROFILE);
 #endif
 		if (unset(PRIVILEGED))
@@ -786,7 +771,7 @@ run_init_scripts(void)
 	    }
 	    if (interact) {
 #ifdef GLOBAL_ZSHRC
-		if (!globalfirst)
+		if (isset(GLOBALRCS))
 		    source(GLOBAL_ZSHRC);
 #endif
 		if (unset(PRIVILEGED))
@@ -794,7 +779,7 @@ run_init_scripts(void)
 	    }
 	    if (islogin) {
 #ifdef GLOBAL_ZLOGIN
-		if (!globalfirst)
+		if (isset(GLOBALRCS))
 		    source(GLOBAL_ZLOGIN);
 #endif
 		if (unset(PRIVILEGED))
diff --git a/Src/options.c b/Src/options.c
index 36030f516..5725d27c0 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -109,7 +109,7 @@ static struct optname optns[] = {
 {NULL, "flowcontrol",	      OPT_ALL,			 FLOWCONTROL},
 {NULL, "functionargzero",     OPT_EMULATE|OPT_NONBOURNE, FUNCTIONARGZERO},
 {NULL, "glob",		      OPT_ALL,			 GLOBOPT},
-{NULL, "globalrcsfirst",      0,			 GLOBALRCSFIRST},
+{NULL, "globalrcs",           OPT_ALL,			 GLOBALRCS},
 {NULL, "globassign",	      OPT_EMULATE|OPT_CSH,	 GLOBASSIGN},
 {NULL, "globcomplete",	      0,			 GLOBCOMPLETE},
 {NULL, "globdots",	      0,			 GLOBDOTS},
@@ -265,7 +265,7 @@ static short zshletters[LAST_OPT - FIRST_OPT + 1] = {
     /* a */  ALLEXPORT,
     /* b */  0,			/* in non-Bourne shells, end of options */
     /* c */  0,			/* command follows */
-    /* d */  GLOBALRCSFIRST,
+    /* d */ -GLOBALRCS,
     /* e */  ERREXIT,
     /* f */ -RCS,
     /* g */  HISTIGNORESPACE,
diff --git a/Src/params.c b/Src/params.c
index e10fbda93..75910c6fd 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -331,6 +331,24 @@ copyparamtable(HashTable ht, char *name)
     return nht;
 }
 
+/* Flag to freeparamnode to unset the struct */
+
+static int delunset;
+
+/* Function to delete a parameter table. */
+
+/**/
+void
+deleteparamtable(HashTable t)
+{
+    /* The parameters in the hash table need to be unset *
+     * before being deleted.                             */
+    int odelunset = delunset;
+    delunset = 1;
+    deletehashtable(t);
+    delunset = odelunset;
+}
+
 static unsigned numparamvals;
 
 /**/
@@ -426,7 +444,7 @@ createparamtable(void)
     Param ip, pm;
     char **new_environ, **envp, **envp2, **sigptr, **t;
     char buf[50], *str, *iname;
-    int num_env;
+    int num_env, oae = opts[ALLEXPORT];
 
     paramtab = realparamtab = newparamtable(151, "paramtab");
 
@@ -446,6 +464,7 @@ createparamtable(void)
 	 * be initialized before we copy the environment variables. *
 	 * We don't want to override whatever values the users has  *
 	 * given them in the environment.                           */
+	opts[ALLEXPORT] = 0;
 	setiparam("MAILCHECK", 60);
 	setiparam("LOGCHECK", 60);
 	setiparam("KEYTIMEOUT", 40);
@@ -491,6 +510,7 @@ createparamtable(void)
 	    }
 	}
 	environ = new_environ;
+	opts[ALLEXPORT] = oae;
 
 	pm = (Param) paramtab->getnode(paramtab, "HOME");
 	if (!(pm->flags & PM_EXPORTED)) {
@@ -1867,24 +1887,14 @@ hashgetfn(Param pm)
     return pm->u.hash;
 }
 
-/* Flag to freeparamnode to unset the struct */
-
-static int delunset;
-
 /* Function to set value of an association parameter */
 
 /**/
 void
 hashsetfn(Param pm, HashTable x)
 {
-    if (pm->u.hash && pm->u.hash != x) {
-	/* The parameters in the hash table need to be unset *
-	 * before being deleted.                             */
-	int odelunset = delunset;
-	delunset = 1;
-	deletehashtable(pm->u.hash);
-	delunset = odelunset;
-    }
+    if (pm->u.hash && pm->u.hash != x)
+	deleteparamtable(pm->u.hash);
     pm->u.hash = x;
 }
 
diff --git a/Src/subst.c b/Src/subst.c
index 3030218d6..47b59e793 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -279,7 +279,7 @@ multsub(char **s, char ***a, int *isarr, char *sep)
 	*p = NULL;
 	if (a && mult_isarr) {
 	    *a = r;
-	    *isarr = 1;
+	    *isarr = SCANPM_MATCHMANY;
 	    mult_isarr = omi;
 	    return 0;
 	}
@@ -976,7 +976,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		zerr("bad substitution", NULL, 0);
 		return NULL;
 	    }
-	} else
+	} else if (INULL(*s))
+	    s++;
+	else
 	    break;
     }
     globsubst = globsubst && !qt;
@@ -999,6 +1001,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    idbeg = val;
 	copied = 1;
 	*s = sav;
+	while (INULL(*s))
+	    s++;
 	v = (Value) NULL;
     } else if (aspar) {
 	if ((v = getvalue(&s, 1))) {
diff --git a/Src/zsh.export b/Src/zsh.export
index 65626e2f6..7595f1d2e 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -44,6 +44,7 @@ deletebuiltins
 deleteconddefs
 deletehashtable
 deleteparamdefs
+deleteparamtable
 deletewrapper
 domatch
 dosetopt
@@ -191,6 +192,7 @@ pwd
 quietgetevent
 quietgethist
 quotedzputs
+readoutput
 realparamtab
 refreshptr
 remlpaths
diff --git a/Src/zsh.h b/Src/zsh.h
index ae4513491..4d46beada 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1158,7 +1158,7 @@ enum {
     EXTENDEDHISTORY,
     FLOWCONTROL,
     FUNCTIONARGZERO,
-    GLOBALRCSFIRST,
+    GLOBALRCS,
     GLOBOPT,
     GLOBASSIGN,
     GLOBCOMPLETE,