about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2007-05-28 22:57:39 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2007-05-28 22:57:39 +0000
commitb0c5f09169ac31855ebf0e93772bb57b9635b380 (patch)
tree410c43a9843b2c88166c2cb9acd531eaa36d036d /Src
parentbd7632079045b1b6d0dee498c40833b409cf757e (diff)
downloadzsh-b0c5f09169ac31855ebf0e93772bb57b9635b380.tar.gz
zsh-b0c5f09169ac31855ebf0e93772bb57b9635b380.tar.xz
zsh-b0c5f09169ac31855ebf0e93772bb57b9635b380.zip
see 23479: add initial features support for modules
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/rlimits.c27
-rw-r--r--Src/Builtins/sched.c28
-rw-r--r--Src/Modules/cap.c30
-rw-r--r--Src/Modules/clone.c30
-rw-r--r--Src/Modules/datetime.c41
-rw-r--r--Src/Modules/example.c35
-rw-r--r--Src/Modules/files.c28
-rw-r--r--Src/Modules/langinfo.c92
-rw-r--r--Src/Modules/langinfo.mdd2
-rw-r--r--Src/Modules/mapfile.c99
-rw-r--r--Src/Modules/mathfunc.c28
-rw-r--r--Src/Modules/parameter.c251
-rw-r--r--Src/Modules/pcre.c41
-rw-r--r--Src/Modules/regex.c30
-rw-r--r--Src/Modules/socket.c29
-rw-r--r--Src/Modules/stat.c29
-rw-r--r--Src/Modules/system.c138
-rw-r--r--Src/Modules/tcp.c28
-rw-r--r--Src/Modules/termcap.c110
-rw-r--r--Src/Modules/terminfo.c112
-rw-r--r--Src/Modules/zftp.c60
-rw-r--r--Src/Modules/zprof.c29
-rw-r--r--Src/Modules/zpty.c30
-rw-r--r--Src/Modules/zselect.c32
-rw-r--r--Src/Modules/zutil.c27
-rw-r--r--Src/Zle/compctl.c28
-rw-r--r--Src/Zle/complete.c33
-rw-r--r--Src/Zle/complist.c27
-rw-r--r--Src/Zle/computil.c28
-rw-r--r--Src/Zle/deltochar.c29
-rw-r--r--Src/Zle/zle_main.c27
-rw-r--r--Src/Zle/zle_thingy.c2
-rw-r--r--Src/Zle/zle_tricky.c2
-rw-r--r--Src/Zle/zleparameter.c118
-rw-r--r--Src/builtin.c31
-rw-r--r--Src/cond.c5
-rw-r--r--Src/exec.c4
-rw-r--r--Src/init.c10
-rw-r--r--Src/mkbltnmlst.sh7
-rw-r--r--Src/mkmakemod.sh2
-rw-r--r--Src/modentry.c10
-rw-r--r--Src/module.c1983
-rw-r--r--Src/params.c45
-rw-r--r--Src/zsh.h91
44 files changed, 2427 insertions, 1441 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 88c7602af..8d55d022d 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -889,6 +889,14 @@ static struct builtin bintab[] = {
     BUILTIN("unlimit", 0, bin_unlimit, 0, -1, 0, "hs", NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -898,16 +906,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return setfeatureenables(m->nam, &module_features, NULL);
     return 0;
 }
 
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index 1d9feab7d..b6b00dff3 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -333,6 +333,14 @@ static struct builtin bintab[] = {
     BUILTIN("sched", 0, bin_sched, 0, -1, 0, NULL, NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -342,10 +350,23 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-    if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
-	return 1;
     addprepromptfn(&checksched);
     return 0;
 }
@@ -364,8 +385,7 @@ cleanup_(Module m)
 	zfree(sch, sizeof(*sch));
     }
     delprepromptfn(&checksched);
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/cap.c b/Src/Modules/cap.c
index 875b8c4e5..2886c42b4 100644
--- a/Src/Modules/cap.c
+++ b/Src/Modules/cap.c
@@ -122,6 +122,14 @@ static struct builtin bintab[] = {
     BUILTIN("setcap", 0, bin_setcap, 2, -1, 0, NULL, NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -131,21 +139,35 @@ setup_(UNUSED(Module m))
 
 /**/
 int
-boot_(Module m)
+features_(Module m, char ***features)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
 }
 
 /**/
 int
-cleanup_(Module m)
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
+boot_(UNUSED(Module m))
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
 
 /**/
 int
+cleanup_(Module m)
+{
+    return setfeatureenables(m->nam, &module_features, NULL);
+}
+
+/**/
+int
 finish_(UNUSED(Module m))
 {
     return 0;
diff --git a/Src/Modules/clone.c b/Src/Modules/clone.c
index cc303d063..adab4cb59 100644
--- a/Src/Modules/clone.c
+++ b/Src/Modules/clone.c
@@ -109,6 +109,14 @@ static struct builtin bintab[] = {
     BUILTIN("clone", 0, bin_clone, 1, 1, 0, NULL, NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -118,21 +126,35 @@ setup_(UNUSED(Module m))
 
 /**/
 int
-boot_(Module m)
+features_(Module m, char ***features)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
 }
 
 /**/
 int
-cleanup_(Module m)
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
+boot_(UNUSED(Module m))
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
 
 /**/
 int
+cleanup_(Module m)
+{
+    return setfeatureenables(m->nam, &module_features, NULL);
+}
+
+/**/
+int
 finish_(UNUSED(Module m))
 {
     return 0;
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index add4b303b..06bf52046 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -154,8 +154,16 @@ static const struct gsu_integer epochseconds_gsu =
 { getcurrentsecs, NULL, stdunsetfn };
 
 static struct paramdef patab[] = {
-    PARAMDEF("EPOCHSECONDS", PM_INTEGER|PM_SPECIAL|PM_READONLY,
-		    NULL, &epochseconds_gsu),
+    SPECIALPMDEF("EPOCHSECONDS", PM_INTEGER|PM_READONLY,
+		 &epochseconds_gsu, NULL, NULL),
+};
+
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    patab, sizeof(patab)/sizeof(*patab),
+    NULL, 0,
+    0
 };
 
 /**/
@@ -167,30 +175,35 @@ setup_(UNUSED(Module m))
 
 /**/
 int
-boot_(Module m)
+features_(Module m, char ***features)
 {
-    return !(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
-	     addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab))
-	    );
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
 }
 
 /**/
 int
-cleanup_(Module m)
+enables_(Module m, int **enables)
 {
-    Param pm;
+    return handlefeatures(m->nam, &module_features, enables);
+}
 
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    pm = (Param) paramtab->getnode(paramtab, "EPOCHSECONDS");
-    if (pm && (pm->node.flags & PM_SPECIAL)) {
-	pm->node.flags &= ~PM_READONLY;
-	unsetparam_pm(pm, 0, 1);
-    }
+/**/
+int
+boot_(Module m)
+{
     return 0;
 }
 
 /**/
 int
+cleanup_(Module m)
+{
+    return setfeatureenables(m->nam, &module_features, NULL);
+}
+
+/**/
+int
 finish_(UNUSED(Module m))
 {
     return 0;
diff --git a/Src/Modules/example.c b/Src/Modules/example.c
index ab3a70592..88e910814 100644
--- a/Src/Modules/example.c
+++ b/Src/Modules/example.c
@@ -184,6 +184,14 @@ static struct funcwrap wrapper[] = {
     WRAPDEF(ex_wrapper),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    cotab, sizeof(cotab)/sizeof(*cotab),
+    patab, sizeof(patab)/sizeof(*patab),
+    mftab, sizeof(mftab)/sizeof(*mftab),
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -195,6 +203,21 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     intparam = 42;
@@ -203,23 +226,15 @@ boot_(Module m)
     arrparam[0] = ztrdup("example");
     arrparam[1] = ztrdup("array");
     arrparam[2] = NULL;
-    return !(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
-	     addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
-	     addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) |
-	     addmathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab)) |
-	     !addwrapper(m, wrapper));
+    return addwrapper(m, wrapper);
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
-    deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
-    deletemathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
     deletewrapper(m, wrapper);
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index 079aeac4d..ba742cc50 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -706,6 +706,14 @@ static struct builtin bintab[] = {
     BUILTIN("sync",  0, bin_sync,  0,  0, 0,         NULL,    NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -715,17 +723,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/langinfo.c b/Src/Modules/langinfo.c
index a09c1a0bb..cfbdeed44 100644
--- a/Src/Modules/langinfo.c
+++ b/Src/Modules/langinfo.c
@@ -30,14 +30,10 @@
 #include "langinfo.mdh"
 #include "langinfo.pro"
 
-static char langinfo_nam[] = "langinfo";
-
 #ifdef HAVE_LANGINFO_H
 # include <langinfo.h>
 #endif
 
-static Param langinfo_pm;
-
 /**/
 #ifdef HAVE_NL_LANGINFO
 
@@ -396,46 +392,6 @@ liitem(char *name)
 }
 
 /**/
-static void
-shempty(void)
-{
-}
-
-/* Create a simple special hash parameter. */
-
-/**/
-static Param
-createlihash()
-{
-    Param pm;
-    HashTable ht;
-
-    unsetparam(langinfo_nam);
-
-    if (!(pm = createparam(langinfo_nam, PM_SPECIAL|PM_HIDE|PM_HIDEVAL|
-			   PM_REMOVABLE|PM_HASHED)))
-	return NULL;
-
-    pm->level = pm->old ? locallevel : 0;
-    pm->gsu.h = &stdhash_gsu;
-    pm->u.hash = ht = newhashtable(7, langinfo_nam, NULL);
-
-    ht->hash        = hasher;
-    ht->emptytable  = (TableFunc) shempty;
-    ht->filltable   = NULL;
-    ht->addnode     = (AddNodeFunc) shempty;
-    ht->getnode     = ht->getnode2 = getlanginfo;
-    ht->removenode  = (RemoveNodeFunc) shempty;
-    ht->disablenode = NULL;
-    ht->enablenode  = NULL;
-    ht->freenode    = (FreeNodeFunc) shempty;
-    ht->printnode   = printparamnode;
-    ht->scantab     = scanlanginfo;
-
-    return (langinfo_pm = pm);
-}
-
-/**/
 static HashNode
 getlanginfo(UNUSED(HashTable ht), char *name)
 {
@@ -490,9 +446,25 @@ scanlanginfo(UNUSED(HashTable ht), ScanFunc func, int flags)
     
 }
 
+static struct paramdef partab[] = {
+    SPECIALPMDEF("langinfo", 0, NULL, getlanginfo, scanlanginfo)
+};
+
 /**/
 #endif /* HAVE_NL_LANGINFO */
 
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+#ifdef HAVE_NL_LANGINFO
+    partab, sizeof(partab)/sizeof(*partab),
+#else
+    NULL, 0,
+#endif
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -502,35 +474,35 @@ setup_(UNUSED(Module m))
 
 /**/
 int
-boot_(UNUSED(Module m))
+features_(Module m, char ***features)
 {
-#ifdef HAVE_NL_LANGINFO
-    if (!createlihash())
-    	return 1;
-#else
-    unsetparam(langinfo_nam);
-#endif
+    *features = featuresarray(m->nam, &module_features);
     return 0;
 }
 
 /**/
 int
-cleanup_(UNUSED(Module m))
+enables_(Module m, int **enables)
 {
-#ifdef HAVE_NL_LANGINFO
-    Param pm;
+    return handlefeatures(m->nam, &module_features, enables);
+}
 
-    if ((pm = (Param) paramtab->getnode(paramtab, langinfo_nam)) &&
-	pm == langinfo_pm) {
-	pm->node.flags &= ~PM_READONLY;
-	unsetparam_pm(pm, 0, 1);
-    }
-#endif
+/**/
+int
+boot_(UNUSED(Module m))
+{
     return 0;
 }
 
 /**/
 int
+cleanup_(UNUSED(Module m))
+{
+    return setfeatureenables(m->nam, &module_features, NULL);
+}
+
+/**/
+int
 finish_(UNUSED(Module m))
 {
     return 0;
diff --git a/Src/Modules/langinfo.mdd b/Src/Modules/langinfo.mdd
index a3a615113..66c4cd452 100644
--- a/Src/Modules/langinfo.mdd
+++ b/Src/Modules/langinfo.mdd
@@ -1,6 +1,6 @@
 name=zsh/langinfo
 
-link=either
+link=`if test x$ac_cv_func_nl_langinfo; then echo either; else echo no; fi`
 load=no
 
 autoparams="langinfo"
diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c
index 9f3ca2612..25b506f03 100644
--- a/Src/Modules/mapfile.c
+++ b/Src/Modules/mapfile.c
@@ -58,60 +58,9 @@
 #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)
-{
-}
-
 static const struct gsu_hash mapfiles_gsu =
 { hashgetfn, setpmmapfiles, stdunsetfn };
 
-/* 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_HIDE|PM_HIDEVAL|
-			   PM_REMOVABLE|PM_HASHED)))
-	return NULL;
-
-    pm->level = pm->old ? locallevel : 0;
-    pm->gsu.h = &mapfiles_gsu;
-    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. */
 
 /**/
@@ -192,9 +141,6 @@ 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;
 
@@ -261,6 +207,10 @@ get_contents(char *fname)
 static const struct gsu_scalar mapfile_gsu =
 { strgetfn, setpmmapfile, unsetpmmapfile };
 
+static struct paramdef partab[] = {
+    SPECIALPMDEF("mapfile", 0, &mapfiles_gsu, getpmmapfile, scanpmmapfile)
+};
+
 /**/
 static HashNode
 getpmmapfile(UNUSED(HashTable ht), char *name)
@@ -272,7 +222,7 @@ getpmmapfile(UNUSED(HashTable ht), char *name)
     pm->node.nam = dupstring(name);
     pm->node.flags = PM_SCALAR;
     pm->gsu.s = &mapfile_gsu;
-    pm->node.flags |= (mapfile_pm->node.flags & PM_READONLY);
+    pm->node.flags |= (partab[0].pm->node.flags & PM_READONLY);
 
     /* Set u.str to contents of file given by name */
     if ((contents = get_contents(pm->node.nam)))
@@ -298,7 +248,7 @@ scanpmmapfile(UNUSED(HashTable ht), ScanFunc func, int flags)
     memset((void *)&pm, 0, sizeof(struct param));
     pm.node.flags = PM_SCALAR;
     pm.gsu.s = &mapfile_gsu;
-    pm.node.flags |= (mapfile_pm->node.flags & PM_READONLY);
+    pm.node.flags |= (partab[0].pm->node.flags & PM_READONLY);
 
     /* Here we scan the current directory, calling func() for each file */
     while ((pm.node.nam = zreaddir(dir, 1))) {
@@ -315,6 +265,14 @@ scanpmmapfile(UNUSED(HashTable ht), ScanFunc func, int flags)
     closedir(dir);
 }
 
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+    partab, sizeof(partab)/sizeof(*partab),
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -324,13 +282,23 @@ setup_(UNUSED(Module m))
 
 /**/
 int
-boot_(UNUSED(Module m))
+features_(Module m, char ***features)
 {
-    /* Create the special associative array. */
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
 
-    if (!createmapfilehash())
-	return 1;
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
 
+/**/
+int
+boot_(UNUSED(Module m))
+{
     return 0;
 }
 
@@ -338,16 +306,7 @@ boot_(UNUSED(Module m))
 int
 cleanup_(UNUSED(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->node.flags &= ~PM_READONLY;
-	unsetparam_pm(pm, 0, 1);
-    }
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/mathfunc.c b/Src/Modules/mathfunc.c
index f6d437ff9..a473476e3 100644
--- a/Src/Modules/mathfunc.c
+++ b/Src/Modules/mathfunc.c
@@ -561,6 +561,14 @@ math_string(UNUSED(char *name), char *arg, int id)
 }
 
 
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    mftab, sizeof(mftab)/sizeof(*mftab),
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -570,17 +578,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-    return !addmathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletemathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 7b790acc6..9d52bcd3f 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -34,46 +34,6 @@
 
 static int incleanup;
 
-/* Empty dummy function for special hash parameters. */
-
-/**/
-static void
-shempty(void)
-{
-}
-
-/* Create a simple special hash parameter. */
-
-/**/
-static Param
-createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan)
-{
-    Param pm;
-    HashTable ht;
-
-    if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_HIDEVAL|
-			   PM_REMOVABLE|PM_HASHED)))
-	return NULL;
-
-    pm->level = pm->old ? locallevel : 0;
-    pm->gsu.h = &stdhash_gsu;
-    pm->u.hash = ht = newhashtable(0, name, NULL);
-
-    ht->hash        = hasher;
-    ht->emptytable  = (TableFunc) shempty;
-    ht->filltable   = NULL;
-    ht->addnode     = (AddNodeFunc) shempty;
-    ht->getnode     = ht->getnode2 = get;
-    ht->removenode  = (RemoveNodeFunc) shempty;
-    ht->disablenode = NULL;
-    ht->enablenode  = NULL;
-    ht->freenode    = (FreeNodeFunc) shempty;
-    ht->printnode   = printparamnode;
-    ht->scantab     = scan;
-
-    return pm;
-}
-
 /* Functions for the parameters special parameter. */
 
 /* Return a string describing the type of a parameter. */
@@ -1838,13 +1798,6 @@ struct pardef {
     Param pm;
 };
 
-/*
- * This is a duplicate of nullsethash_gsu.  On some systems
- * (such as Cygwin) we can't put a pointer to an imported variable
- * in a compile-time initialiser, so we use this instead.
- */
-static const struct gsu_hash pmnullsethash_gsu =
-{ hashgetfn, nullsethashfn, nullunsetfn };
 static const struct gsu_hash pmcommands_gsu =
 { hashgetfn, setpmcommands, stdunsetfn };
 static const struct gsu_hash pmfunctions_gsu =
@@ -1881,149 +1834,117 @@ static const struct gsu_array dirs_gsu =
 static const struct gsu_array historywords_gsu =
 { histwgetfn, arrsetfn, stdunsetfn };
 
-static struct pardef partab[] = {
-    { "parameters", PM_READONLY,
-      getpmparameter, scanpmparameters, &pmnullsethash_gsu,
-      NULL, NULL },
-    { "commands", 0,
-      getpmcommand, scanpmcommands, &pmcommands_gsu,
-      NULL, NULL },
-    { "functions", 0,
-      getpmfunction, scanpmfunctions, &pmfunctions_gsu,
-      NULL, NULL },
-    { "dis_functions", 0,
-      getpmdisfunction, scanpmdisfunctions, &pmdisfunctions_gsu,
-      NULL, NULL },
-    { "funcstack", PM_ARRAY|PM_SPECIAL|PM_READONLY,
-      NULL, NULL, NULL,
-      &funcstack_gsu, NULL },
-    { "functrace", PM_ARRAY|PM_SPECIAL|PM_READONLY,
-      NULL, NULL, NULL,
-      &functrace_gsu, NULL },
-    { "builtins", PM_READONLY,
-      getpmbuiltin, scanpmbuiltins, NULL,
-      NULL, NULL },
-    { "dis_builtins", PM_READONLY,
-      getpmdisbuiltin, scanpmdisbuiltins, NULL,
-      NULL, NULL },
-    { "reswords", PM_ARRAY|PM_SPECIAL|PM_READONLY,
-      NULL, NULL, NULL,
-      &reswords_gsu, NULL },
-    { "dis_reswords", PM_ARRAY|PM_SPECIAL|PM_READONLY,
-      NULL, NULL, NULL,
-      &disreswords_gsu, NULL },
-    { "options", 0,
-      getpmoption, scanpmoptions, &pmoptions_gsu,
-      NULL, NULL },
-    { "modules", PM_READONLY,
-      getpmmodule, scanpmmodules, NULL,
-      NULL, NULL },
-    { "dirstack", PM_ARRAY|PM_SPECIAL|PM_REMOVABLE,
-      NULL, NULL, NULL,
-      &dirs_gsu, NULL },
-    { "history", PM_READONLY,
-      getpmhistory, scanpmhistory, NULL,
-      NULL, NULL,  },
-    { "historywords", PM_ARRAY|PM_SPECIAL|PM_READONLY,
-      NULL, NULL, NULL,
-      &historywords_gsu, NULL },
-    { "jobtexts", PM_READONLY,
-      getpmjobtext, scanpmjobtexts, NULL,
-      NULL, NULL },
-    { "jobstates", PM_READONLY,
-      getpmjobstate, scanpmjobstates, NULL,
-      NULL, NULL },
-    { "jobdirs", PM_READONLY,
-      getpmjobdir, scanpmjobdirs, NULL,
-      NULL, NULL },
-    { "nameddirs", 0,
-      getpmnameddir, scanpmnameddirs, &pmnameddirs_gsu,
-      NULL, NULL },
-    { "userdirs", PM_READONLY,
-      getpmuserdir, scanpmuserdirs, NULL,
-      NULL, NULL },
-    { "aliases", 0,
-      getpmralias, scanpmraliases, &pmraliases_gsu,
-      NULL, NULL },
-    { "galiases", 0,
-      getpmgalias, scanpmgaliases, &pmgaliases_gsu,
-      NULL, NULL },
-    { "saliases", 0,
-      getpmsalias, scanpmsaliases, &pmsaliases_gsu,
-      NULL, NULL },
-    { "dis_aliases", 0,
-      getpmdisralias, scanpmdisraliases, &pmdisraliases_gsu,
-      NULL, NULL },
-    { "dis_galiases", 0,
-      getpmdisgalias, scanpmdisgaliases, &pmdisgaliases_gsu,
-      NULL, NULL },
-    { "dis_saliases", 0,
-      getpmdissalias, scanpmdissaliases, &pmdissaliases_gsu,
-      NULL, NULL },
-    { NULL, 0, NULL, NULL, NULL, NULL, NULL }
+static struct paramdef partab[] = {
+    SPECIALPMDEF("parameters", PM_READONLY,
+	    NULL, getpmparameter, scanpmparameters),
+    SPECIALPMDEF("commands", 0, &pmcommands_gsu, getpmcommand, scanpmcommands),
+    SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction,
+		 scanpmfunctions),
+    SPECIALPMDEF("dis_functions", 0, 
+	    &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions),
+    SPECIALPMDEF("funcstack", PM_ARRAY|PM_READONLY,
+	    &funcstack_gsu, NULL, NULL),
+    SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY,
+	    &functrace_gsu, NULL, NULL),
+    SPECIALPMDEF("builtins", PM_READONLY, NULL, getpmbuiltin, scanpmbuiltins),
+    SPECIALPMDEF("dis_builtins", PM_READONLY,
+	    NULL, getpmdisbuiltin, scanpmdisbuiltins),
+    SPECIALPMDEF("reswords", PM_ARRAY|PM_READONLY,
+	    &reswords_gsu, NULL, NULL),
+    SPECIALPMDEF("dis_reswords", PM_ARRAY|PM_READONLY,
+	    &disreswords_gsu, NULL, NULL),
+    SPECIALPMDEF("options", 0,
+	    &pmoptions_gsu, getpmoption, scanpmoptions),
+    SPECIALPMDEF("modules", PM_READONLY,
+	    NULL, getpmmodule, scanpmmodules),
+    SPECIALPMDEF("dirstack", PM_ARRAY,
+	    &dirs_gsu, NULL, NULL),
+    SPECIALPMDEF("history", PM_READONLY,
+	    NULL, getpmhistory, scanpmhistory),
+    SPECIALPMDEF("historywords", PM_ARRAY|PM_READONLY,
+	    &historywords_gsu, NULL, NULL),
+    SPECIALPMDEF("jobtexts", PM_READONLY,
+	    NULL, getpmjobtext, scanpmjobtexts),
+    SPECIALPMDEF("jobstates", PM_READONLY,
+	    NULL, getpmjobstate, scanpmjobstates),
+    SPECIALPMDEF("jobdirs", PM_READONLY,
+	    NULL, getpmjobdir, scanpmjobdirs),
+    SPECIALPMDEF("nameddirs", 0,
+	    &pmnameddirs_gsu, getpmnameddir, scanpmnameddirs),
+    SPECIALPMDEF("userdirs", PM_READONLY,
+	    NULL, getpmuserdir, scanpmuserdirs),
+    SPECIALPMDEF("aliases", 0,
+	    &pmraliases_gsu, getpmralias, scanpmraliases),
+    SPECIALPMDEF("galiases", 0,
+	    &pmgaliases_gsu, getpmgalias, scanpmgaliases),
+    SPECIALPMDEF("saliases", 0,
+	    &pmsaliases_gsu, getpmsalias, scanpmsaliases),
+    SPECIALPMDEF("dis_aliases", 0,
+	    &pmdisraliases_gsu, getpmdisralias, scanpmdisraliases),
+    SPECIALPMDEF("dis_galiases", 0,
+	    &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases),
+    SPECIALPMDEF("dis_saliases", 0,
+	    &pmdissaliases_gsu, getpmdissalias, scanpmdissaliases)
+};
+
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+    partab, sizeof(partab)/sizeof(*partab),
+    NULL, 0,
+    0
 };
 
 /**/
 int
 setup_(UNUSED(Module m))
 {
-    incleanup = 0;
-
     return 0;
 }
 
 /**/
 int
-boot_(UNUSED(Module m))
+features_(Module m, char ***features)
 {
-    /* Create the special associative arrays.
-     * As an example for autoloaded parameters, this is probably a bad
-     * example, because the zsh core doesn't support creation of
-     * special hashes, yet. */
-
-    struct pardef *def;
-
-    for (def = partab; def->name; def++) {
-	unsetparam(def->name);
-
-	if (def->getnfn) {
-	    if (!(def->pm = createspecialhash(def->name, def->getnfn,
-					      def->scantfn)))
-		return 1;
-	    def->pm->node.flags |= def->flags;
-	    if (def->hash_gsu)
-		def->pm->gsu.h = def->hash_gsu;
-	} else {
-	    if (!(def->pm = createparam(def->name, def->flags | PM_HIDE|
-					PM_HIDEVAL | PM_REMOVABLE)))
-		return 1;
-	    def->pm->gsu.a = def->array_gsu;
-	}
-    }
+    *features = featuresarray(m->nam, &module_features);
     return 0;
 }
 
 /**/
 int
-cleanup_(UNUSED(Module m))
+enables_(Module m, int **enables)
 {
-    Param pm;
-    struct pardef *def;
-
+    int ret;
+    /*
+     * If we remove features, we shouldn't have an effect
+     * on the main shell, so set the flag to indicate.
+     */
     incleanup = 1;
+    ret = handlefeatures(m->nam, &module_features, enables);
+    incleanup = 0;
+    return ret;
+}
 
-    for (def = partab; def->name; def++) {
-	if ((pm = (Param) paramtab->getnode(paramtab, def->name)) &&
-	    pm == def->pm) {
-	    pm->node.flags &= ~PM_READONLY;
-	    unsetparam_pm(pm, 0, 1);
-	}
-    }
+/**/
+int
+boot_(Module m)
+{
     return 0;
 }
 
 /**/
 int
+cleanup_(Module m)
+{
+    int ret;
+    incleanup = 1;
+    ret = setfeatureenables(m->nam, &module_features, NULL);
+    incleanup = 0;
+    return ret;
+}
+
+/**/
+int
 finish_(UNUSED(Module m))
 {
     return 0;
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index d067d8949..45c38eba0 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -295,6 +295,19 @@ static struct builtin bintab[] = {
 };
 
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
+    cotab, sizeof(cotab)/sizeof(*cotab),
+#else /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
+    NULL, 0,
+#endif /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -304,25 +317,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) ||
-	   !addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
-#else /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-#endif /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
-    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
-#endif /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c
index 44019a1b9..a3d956055 100644
--- a/Src/Modules/regex.c
+++ b/Src/Modules/regex.c
@@ -131,6 +131,16 @@ static struct conddef cotab[] = {
     CONDDEF("regex-match", CONDF_INFIX, zcond_regex_match, 0, 0, ZREGEX_EXTENDED)
 };
 
+
+static struct features module_features = {
+    NULL, 0,
+    cotab, sizeof(cotab)/sizeof(*cotab),
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -140,17 +150,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-    return !addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/socket.c b/Src/Modules/socket.c
index 7ca56b4e7..413625dcf 100644
--- a/Src/Modules/socket.c
+++ b/Src/Modules/socket.c
@@ -255,6 +255,14 @@ static struct builtin bintab[] = {
     BUILTIN("zsocket", 0, bin_zsocket, 0, 3, 0, "ad:ltv", NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /* The load/unload routines required by the zsh library interface */
 
 /**/
@@ -266,18 +274,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
-boot_(Module m)
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return handlefeatures(m->nam, &module_features, enables);
 }
 
+/**/
+int
+boot_(Module m)
+{
+    return 0;
+}
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 3ffaf9d4d..1d55317ea 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -621,6 +621,15 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func))
 
 static struct builtin bintab[] = {
     BUILTIN("stat", 0, bin_stat, 0, -1, 0, NULL, NULL),
+    BUILTIN("zstat", 0, bin_stat, 0, -1, 0, NULL, NULL),
+};
+
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
 };
 
 /**/
@@ -632,17 +641,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index 1eaa2fabd..f8a188d42 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -364,47 +364,70 @@ static const struct gsu_array errnos_gsu =
 /* Functions for the sysparams special parameter. */
 
 /**/
-static char *
-sysparamgetfn(Param pm)
+static void
+fillpmsysparams(Param pm, char *name)
 {
     char buf[DIGBUFSIZE];
     int num;
 
-    if (!strcmp(pm->node.nam, "pid")) {
+    pm->node.nam = dupstring(name);
+    pm->node.flags = PM_SCALAR | PM_READONLY;
+    pm->gsu.s = &nullsetscalar_gsu;
+    if (!strcmp(name, "pid")) {
 	num = (int)getpid();
-    } else if (!strcmp(pm->node.nam, "ppid")) {
+    } else if (!strcmp(name, "ppid")) {
 	num = (int)getppid();
-    }
-    else {
-#ifdef DEBUG
-	dputs("Bad sysparam parameter");
-#endif
-	return "";
+    } else {
+	pm->u.str = dupstring("");
+	pm->node.flags |= PM_UNSET;
+	return;
     }
 
     sprintf(buf, "%d", num);
-    return dupstring(buf);
+    pm->u.str = dupstring(buf);
 }
 
-static const struct gsu_scalar sysparam_gsu =
-{ sysparamgetfn, strsetfn, stdunsetfn };
 
+/**/
+static HashNode
+getpmsysparams(UNUSED(HashTable ht), char *name)
+{
+    Param pm;
+
+    pm = (Param) hcalloc(sizeof(struct param));
+    fillpmsysparams(pm, name);
+    return &pm->node;
+}
+
+
+/**/
 static void
-fixsysparams(HashNode hn, int flags)
+scanpmsysparams(UNUSED(HashTable ht), ScanFunc func, int flags)
 {
-    Param pm = (Param)hn;
+    struct param spm;
 
-    if (flags) {
-	/* prepare to free */
-	pm->node.flags &= ~PM_READONLY;
-    } else {
-	/* assign */
-	pm->gsu.s = &sysparam_gsu;
-	pm->node.flags |= PM_READONLY;
-    }
+    fillpmsysparams(&spm, "pid");
+    func(&spm.node, flags);
+    fillpmsysparams(&spm, "ppid");
+    func(&spm.node, flags);
 }
 
 
+static struct paramdef partab[] = {
+    SPECIALPMDEF("errnos", PM_ARRAY|PM_READONLY,
+		 &errnos_gsu, NULL, NULL),
+    SPECIALPMDEF("sysparams", PM_READONLY,
+		 NULL, getpmsysparams, scanpmsysparams)
+};
+
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    partab, sizeof(partab)/sizeof(*partab),
+    NULL, 0,
+    0
+};
+
 /* The load/unload routines required by the zsh library interface */
 
 /**/
@@ -415,61 +438,24 @@ setup_(UNUSED(Module m))
 }
 
 /**/
-static void
-tidyparam(Param pm)
+int
+features_(Module m, char ***features)
 {
-    if (!pm)
-	return;
-    pm->node.flags &= ~PM_READONLY;
-    unsetparam_pm(pm, 0, 1);
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
 }
 
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
 
 /**/
 int
 boot_(Module m)
 {
-    Param pm_nos, pm_params;
-    HashTable ht;
-    const char *sysparams_args[] = {
-	"pid", 	"ppid", NULL
-    }, **srcptr;
-    char **arglist, **dstptr;
-
-    /* this takes care of an autoload on errnos */
-    unsetparam("errnos");
-    if (!(pm_nos = createparam("errnos", PM_ARRAY|PM_SPECIAL|PM_READONLY|
-			       PM_HIDE|PM_HIDEVAL|PM_REMOVABLE)))
-	return 1;
-    pm_nos->gsu.a = &errnos_gsu;
-
-    if (!(pm_params = createparam("sysparams", PM_HASHED|PM_SPECIAL|
-				  PM_HIDE|PM_HIDEVAL|PM_REMOVABLE))) {
-	tidyparam(pm_nos);
-	return 1;
-    }
-    pm_params->level = pm_params->old ? locallevel : 0;
-    pm_params->gsu.h = &stdhash_gsu;
-    pm_params->u.hash = ht = newparamtable(0, "sysparams");
-
-    arglist = (char **)zshcalloc((2*arrlen((char **)sysparams_args) + 1) *
-			       sizeof(char *));
-    for (srcptr = sysparams_args, dstptr = arglist; *srcptr; ) {
-	*dstptr++ = ztrdup(*srcptr++);
-	*dstptr++ = ztrdup("");
-    }
-    *dstptr = NULL;
-    /* make sure we don't overwrite the hash table: use the "augment" arg */
-    arrhashsetfn(pm_params, arglist, 1);
-    scanhashtable(ht, 0, 0, 0, fixsysparams, 0);
-
-    pm_params->node.flags |= PM_READONLY;
-
-    if (!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab))) {
-	tidyparam(pm_nos);
-	tidyparam(pm_params);
-	return 1;
-    }
     return 0;
 }
 
@@ -478,17 +464,7 @@ boot_(Module m)
 int
 cleanup_(Module m)
 {
-    Param pm;
-    if ((pm = (Param)paramtab->getnode(paramtab, "errnos")))
-	tidyparam(pm);
-    if ((pm = (Param)paramtab->getnode(paramtab, "sysparams")))
-    {
-	scanhashtable(pm->u.hash, 0, 0, 0, fixsysparams, 1);
-	tidyparam(pm);
-    }
-
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
index 2484edfe3..86803e952 100644
--- a/Src/Modules/tcp.c
+++ b/Src/Modules/tcp.c
@@ -675,6 +675,14 @@ static struct builtin bintab[] = {
     BUILTIN("ztcp", 0, bin_ztcp, 0, 3, 0, "acd:flLtv", NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /* The load/unload routines required by the zsh library interface */
 
 /**/
@@ -686,10 +694,25 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     ztcp_sessions = znewlinklist();
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
 }
 
 
@@ -698,9 +721,8 @@ int
 cleanup_(Module m)
 {
     tcp_cleanup();
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     freelinklist(ztcp_sessions, (FreeFunc) ztcp_free_session);
-    return 0;
+    return setfeatureeanbles(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/termcap.c b/Src/Modules/termcap.c
index e29b2bf74..c19db4892 100644
--- a/Src/Modules/termcap.c
+++ b/Src/Modules/termcap.c
@@ -48,8 +48,7 @@
 #include "termcap.mdh"
 #include "termcap.pro"
 
-static char termcap_nam[] = "termcap";
-
+/**/
 #ifdef HAVE_TGETENT
 # ifdef USES_TERM_H
 #  ifdef HAVE_TERMIO_H
@@ -65,8 +64,6 @@ static char termcap_nam[] = "termcap";
 #  endif
 # endif
 
-static Param termcap_pm;
-
 #ifndef HAVE_BOOLCODES
 static char *boolcodes[] = {
     "bw", "am", "ut", "cc", "xs", "YA", "YF", "YB", "xt", "xn", "eo",
@@ -161,62 +158,11 @@ bin_echotc(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     return 0;
 }
 
-#else /* ! HAVE_TGETENT */
-
-#define bin_echotc bin_notavail
-
-#endif /* HAVE_TGETENT */
-
 static struct builtin bintab[] = {
     BUILTIN("echotc", 0, bin_echotc, 1, -1, 0, NULL, NULL),
 };
 
 /**/
-#ifdef HAVE_TGETENT
-
-/* Empty dummy function for special hash parameters. */
-
-/**/
-static void
-shempty(void)
-{
-}
-
-/* Create a simple special hash parameter. */
-
-/**/
-static Param
-createtchash()
-{
-    Param pm;
-    HashTable ht;
-
-    unsetparam(termcap_nam);
-
-    if (!(pm = createparam(termcap_nam, PM_SPECIAL|PM_HIDE|PM_HIDEVAL|
-			   PM_REMOVABLE|PM_HASHED)))
-	return NULL;
-
-    pm->level = pm->old ? locallevel : 0;
-    pm->gsu.h = &stdhash_gsu;
-    pm->u.hash = ht = newhashtable(7, termcap_nam, NULL);
-
-    ht->hash        = hasher;
-    ht->emptytable  = (TableFunc) shempty;
-    ht->filltable   = NULL;
-    ht->addnode     = (AddNodeFunc) shempty;
-    ht->getnode     = ht->getnode2 = gettermcap;
-    ht->removenode  = (RemoveNodeFunc) shempty;
-    ht->disablenode = NULL;
-    ht->enablenode  = NULL;
-    ht->freenode    = (FreeNodeFunc) shempty;
-    ht->printnode   = printparamnode;
-    ht->scantab     = scantermcap;
-
-    return (termcap_pm = pm);
-}
-
-/**/
 static HashNode
 gettermcap(UNUSED(HashTable ht), char *name)
 {
@@ -364,9 +310,29 @@ scantermcap(UNUSED(HashTable ht), ScanFunc func, int flags)
     }
 }
 
+struct paramdef partab[] = {
+    SPECIALPMDEF("termcap", PM_READONLY, NULL, gettermcap, scantermcap)
+};
+
 /**/
 #endif /* HAVE_TGETENT */
 
+static struct features module_features = {
+#ifdef HAVE_TGETENT
+    bintab, sizeof(bintab)/sizeof(*bintab),
+#else
+    NULL, 0,
+#endif
+    NULL, 0,
+#ifdef HAVE_TGETENT
+    partab, sizeof(partab)/sizeof(*partab),
+#else
+    NULL, 0,
+#endif
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -376,36 +342,36 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
 #ifdef HAVE_TGETENT
 # ifdef HAVE_SETUPTERM
     setupterm((char *)0, 1, (int *)0);
 # endif
-
-    if (!createtchash())
-    	return 1;
-#else
-    unsetparam(termcap_nam);
 #endif
-    return  !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return  0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-#ifdef HAVE_TGETENT
-    Param pm;
-
-    if ((pm = (Param) paramtab->getnode(paramtab, termcap_nam)) &&
-	pm == termcap_pm) {
-	pm->node.flags &= ~PM_READONLY;
-	unsetparam_pm(pm, 0, 1);
-    }
-#endif
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c
index b4a1c599b..d324c3a6c 100644
--- a/Src/Modules/terminfo.c
+++ b/Src/Modules/terminfo.c
@@ -37,7 +37,6 @@
 #endif
 
 #include "terminfo.pro"
-static char terminfo_nam[] = "terminfo";
 
 /**/
 #ifdef USE_TERMINFO_MODULE
@@ -55,8 +54,6 @@ static char terminfo_nam[] = "terminfo";
 #  include <term.h>
 # endif
 
-static Param terminfo_pm;
-
 /* echoti: output a terminfo capability */
 
 /**/
@@ -126,64 +123,11 @@ bin_echoti(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     return 0;
 }
 
-/**/
-#else /* !USE_TERMINFO_MODULE */
-
-#define bin_echoti bin_notavail
-
-/**/
-#endif /* !USE_TERMINFO_MODULE */
-
 static struct builtin bintab[] = {
     BUILTIN("echoti", 0, bin_echoti, 1, -1, 0, NULL, NULL),
 };
 
 /**/
-#ifdef USE_TERMINFO_MODULE
-
-/* Empty dummy function for special hash parameters. */
-
-/**/
-static void
-shempty(void)
-{
-}
-
-/* Create a simple special hash parameter. */
-
-/**/
-static Param
-createtihash()
-{
-    Param pm;
-    HashTable ht;
-
-    unsetparam(terminfo_nam);
-
-    if (!(pm = createparam(terminfo_nam, PM_SPECIAL|PM_HIDE|PM_HIDEVAL|
-			   PM_REMOVABLE|PM_HASHED)))
-	return NULL;
-
-    pm->level = pm->old ? locallevel : 0;
-    pm->gsu.h = &stdhash_gsu;
-    pm->u.hash = ht = newhashtable(7, terminfo_nam, NULL);
-
-    ht->hash        = hasher;
-    ht->emptytable  = (TableFunc) shempty;
-    ht->filltable   = NULL;
-    ht->addnode     = (AddNodeFunc) shempty;
-    ht->getnode     = ht->getnode2 = getterminfo;
-    ht->removenode  = (RemoveNodeFunc) shempty;
-    ht->disablenode = NULL;
-    ht->enablenode  = NULL;
-    ht->freenode    = (FreeNodeFunc) shempty;
-    ht->printnode   = printparamnode;
-    ht->scantab     = scanterminfo;
-
-    return (terminfo_pm = pm);
-}
-
-/**/
 static HashNode
 getterminfo(UNUSED(HashTable ht), char *name)
 {
@@ -339,9 +283,30 @@ scanterminfo(UNUSED(HashTable ht), ScanFunc func, int flags)
     }
 }
 
+static struct paramdef partab[] = {
+    SPECIALPMDEF("terminfo", PM_READONLY, NULL,
+		 getterminfo, scanterminfo)
+};
+
 /**/
 #endif /* USE_TERMINFO_MODULE */
 
+static struct features module_features = {
+#ifdef USE_TERMINFO_MODULE
+    bintab, sizeof(bintab)/sizeof(*bintab),
+#else
+    NULL, 0,
+#endif
+    NULL, 0,
+#ifdef USE_TERMINFO_MODULE
+    partab, sizeof(partab)/sizeof(*partab),
+#else
+    NULL, 0,
+#endif
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -351,6 +316,21 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
 #ifdef USE_TERMINFO_MODULE
@@ -360,30 +340,16 @@ boot_(Module m)
     if (setupterm((char *)0, 1, &errret) == ERR)
 	return 1;
 # endif
-
-    if (!createtihash())
-    	return 1;
-#else
-    unsetparam(terminfo_nam);
 #endif
-    return  !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-#ifdef USE_TERMINFO_MODULE
-    Param pm;
-
-    if ((pm = (Param) paramtab->getnode(paramtab, terminfo_nam)) &&
-	pm == terminfo_pm) {
-	pm->node.flags &= ~PM_READONLY;
-	unsetparam_pm(pm, 0, 1);
-    }
-#endif
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 8c9cdf735..89b869592 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -3151,7 +3151,6 @@ zftp_cleanup(void)
     zfunsetparam("ZFTP_SESSION");
     freelinklist(zfsessions, (FreeFunc) freesession);
     zfree(zfstatusp, sizeof(int)*zfsesscnt);
-    deletebuiltins("zftp", bintab, sizeof(bintab)/sizeof(*bintab));
 }
 
 static int
@@ -3161,47 +3160,68 @@ zftpexithook(UNUSED(Hookdef d), UNUSED(void *dummy))
     return 0;
 }
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /* The load/unload routines required by the zsh library interface */
 
 /**/
 int
 setup_(UNUSED(Module m))
 {
-    /* setup_ returns 0 for success. require_module returns 1 for success. */
-    return !require_module("", "zsh/net/tcp", 0, 0);
+    return (require_module("", "zsh/net/tcp", NULL) == 1);
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
 }
 
 /**/
 int
 boot_(UNUSED(Module m))
 {
-    int ret;
-    if ((ret = addbuiltins("zftp", bintab,
-			   sizeof(bintab)/sizeof(*bintab))) == 1) {
-	/* if successful, set some default parameters */
-	off_t tmout_def = 60;
-	zfsetparam("ZFTP_VERBOSE", ztrdup("450"), ZFPM_IFUNSET);
-	zfsetparam("ZFTP_TMOUT", &tmout_def, ZFPM_IFUNSET|ZFPM_INTEGER);
-	zfsetparam("ZFTP_PREFS", ztrdup("PS"), ZFPM_IFUNSET);
-	/* default preferences if user deletes variable */
-	zfprefs = ZFPF_SNDP|ZFPF_PASV;
+    /*
+     * Set some default parameters.
+     * These aren't special, so aren't associated with features.
+     */
+    off_t tmout_def = 60;
+    zfsetparam("ZFTP_VERBOSE", ztrdup("450"), ZFPM_IFUNSET);
+    zfsetparam("ZFTP_TMOUT", &tmout_def, ZFPM_IFUNSET|ZFPM_INTEGER);
+    zfsetparam("ZFTP_PREFS", ztrdup("PS"), ZFPM_IFUNSET);
+    /* default preferences if user deletes variable */
+    zfprefs = ZFPF_SNDP|ZFPF_PASV;
     
-	zfsessions = znewlinklist();
-	newsession("default");
+    zfsessions = znewlinklist();
+    newsession("default");
 
-	addhookfunc("exit", zftpexithook);
-    }
+    addhookfunc("exit", zftpexithook);
 
-    return !ret;
+    return 0;
 }
 
 /**/
 int
-cleanup_(UNUSED(Module m))
+cleanup_(Module m)
 {
     deletehookfunc("exit", zftpexithook);
     zftp_cleanup();
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c
index ca053a9e9..b30e44432 100644
--- a/Src/Modules/zprof.c
+++ b/Src/Modules/zprof.c
@@ -295,6 +295,14 @@ static struct funcwrap wrapper[] = {
     WRAPDEF(zprof_wrapper),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(Module m)
@@ -305,6 +313,21 @@ setup_(Module m)
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     calls = NULL;
@@ -312,8 +335,7 @@ boot_(Module m)
     arcs = NULL;
     narcs = 0;
     stack = NULL;
-    return !(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
-	     !addwrapper(m, wrapper));
+    return addwrapper(m, wrapper);
 }
 
 /**/
@@ -322,9 +344,8 @@ cleanup_(Module m)
 {
     freepfuncs(calls);
     freeparcs(arcs);
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     deletewrapper(m, wrapper);
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 744548f50..3280b8175 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -725,10 +725,20 @@ ptyhook(UNUSED(Hookdef d), UNUSED(void *dummy))
     return 0;
 }
 
+
 static struct builtin bintab[] = {
     BUILTIN("zpty", 0, bin_zpty, 0, -1, 0, "ebdrwLnt", NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -738,12 +748,27 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     ptycmds = NULL;
 
     addhookfunc("exit", ptyhook);
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
 }
 
 /**/
@@ -752,8 +777,7 @@ cleanup_(Module m)
 {
     deletehookfunc("exit", ptyhook);
     deleteallptycmds();
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Modules/zselect.c b/Src/Modules/zselect.c
index 4e547a47e..3e71fa3bd 100644
--- a/Src/Modules/zselect.c
+++ b/Src/Modules/zselect.c
@@ -267,10 +267,20 @@ bin_zselect(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 #endif
 }
 
+
 static struct builtin bintab[] = {
     BUILTIN("zselect", 0, bin_zselect, 0, -1, 0, NULL, NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
+
 /* The load/unload routines required by the zsh library interface */
 
 /**/
@@ -282,20 +292,34 @@ setup_(UNUSED(Module m))
 
 /**/
 int
-boot_(Module m)
+features_(Module m, char ***features)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
 }
 
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
 
 /**/
 int
-cleanup_(UNUSED(Module m))
+boot_(Module m)
 {
-    deletebuiltins("zselect", bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
 
+
+/**/
+int
+cleanup_(Module m)
+{
+    return setfeatureenables(m->nam, &module_features, NULL);
+}
+
 /**/
 int
 finish_(UNUSED(Module m))
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 1e4bf975a..a4172c581 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -1740,6 +1740,13 @@ static struct builtin bintab[] = {
     BUILTIN("zparseopts", 0, bin_zparseopts, 1, -1, 0, NULL, NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
 
 /**/
 int
@@ -1752,17 +1759,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 81f11b423..bafbb2f68 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -3942,6 +3942,14 @@ static struct builtin bintab[] = {
     BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -3964,11 +3972,26 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     addhookfunc("compctl_make", (Hookfn) ccmakehookfn);
     addhookfunc("compctl_cleanup", (Hookfn) cccleanuphookfn);
-    return (addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) != 1);
+    return 0;
 }
 
 /**/
@@ -3977,8 +4000,7 @@ cleanup_(Module m)
 {
     deletehookfunc("compctl_make", (Hookfn) ccmakehookfn);
     deletehookfunc("compctl_cleanup", (Hookfn) cccleanuphookfn);
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index a73d3cf14..aac760a92 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -1467,6 +1467,14 @@ struct hookdef comphooks[] = {
     HOOKDEF("comp_list_matches", ilistmatches, 0),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    cotab, sizeof(cotab)/sizeof(*cotab),
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -1492,6 +1500,21 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     addhookfunc("complete", (Hookfn) do_completion);
@@ -1502,11 +1525,7 @@ boot_(Module m)
     addhookfunc("list_matches", (Hookfn) list_matches);
     addhookfunc("invalidate_list", (Hookfn) invalidate_list);
     addhookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks));
-    if (!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
-	  addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
-	  !addwrapper(m, wrapper)))
-	return 1;
-    return 0;
+    return addwrapper(m, wrapper);
 }
 
 /**/
@@ -1521,10 +1540,8 @@ cleanup_(Module m)
     deletehookfunc("list_matches", (Hookfn) list_matches);
     deletehookfunc("invalidate_list", (Hookfn) invalidate_list);
     deletehookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks));
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
     deletewrapper(m, wrapper);
-    return 0;
+    return seteatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index cedccc254..787b7b25c 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -3219,6 +3219,14 @@ menuselect(char **args)
     return 0;
 }
 
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -3228,6 +3236,21 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     mtab = NULL;
@@ -3269,7 +3292,7 @@ boot_(Module m)
 
 /**/
 int
-cleanup_(UNUSED(Module m))
+cleanup_(Module m)
 {
     free(mtab);
     free(mgtab);
@@ -3279,7 +3302,7 @@ cleanup_(UNUSED(Module m))
     deletehookfunc("menu_start", (Hookfn) domenuselect);
     unlinkkeymap("menuselect", 1);
     unlinkkeymap("listscroll", 1);
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index dbbaa61e2..0d8cf5364 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -4567,6 +4567,14 @@ static struct builtin bintab[] = {
     BUILTIN("compgroups", 0, bin_compgroups, 1, -1, 0, NULL, NULL),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 
 /**/
 int
@@ -4584,17 +4592,31 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
 }
 
 /**/
 int
 cleanup_(Module m)
 {
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c
index e7bfabfd3..0c64cf18d 100644
--- a/Src/Zle/deltochar.c
+++ b/Src/Zle/deltochar.c
@@ -74,6 +74,16 @@ deltochar(UNUSED(char **args))
     return !ok;
 }
 
+
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -83,6 +93,21 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     w_deletetochar = addzlefunction("delete-to-char", deltochar,
@@ -100,11 +125,11 @@ boot_(Module m)
 
 /**/
 int
-cleanup_(UNUSED(Module m))
+cleanup_(Module m)
 {
     deletezlefunction(w_deletetochar);
     deletezlefunction(w_zaptochar);
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 9f71f692b..8ab10e5de 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1777,6 +1777,14 @@ mod_export struct hookdef zlehooks[] = {
     HOOKDEF("invalidate_list", NULL, 0),
 };
 
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    0
+};
+
 /**/
 int
 setup_(UNUSED(Module m))
@@ -1817,11 +1825,25 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m->nam, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
 boot_(Module m)
 {
     addhookfunc("before_trap", (Hookfn) zlebeforetrap);
     addhookfunc("after_trap", (Hookfn) zleaftertrap);
-    addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     addhookdefs(m->nam, zlehooks, sizeof(zlehooks)/sizeof(*zlehooks));
     return 0;
 }
@@ -1836,9 +1858,8 @@ cleanup_(Module m)
     }
     deletehookfunc("before_trap", (Hookfn) zlebeforetrap);
     deletehookfunc("after_trap", (Hookfn) zleaftertrap);
-    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     deletehookdefs(m->nam, zlehooks, sizeof(zlehooks)/sizeof(*zlehooks));
-    return 0;
+    return setfeatureenables(m->nam, &module_features, NULL);
 }
 
 /**/
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index debd31a28..42e32f142 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -590,7 +590,7 @@ bin_zle_complete(char *name, char **args, UNUSED(Options ops), UNUSED(char func)
     Thingy t;
     Widget w, cw;
 
-    if (!require_module(name, "zsh/complete", 0, 0)) {
+    if (require_module(name, "zsh/complete", NULL) == 1) {
 	zwarnnam(name, "can't load complete module");
 	return 1;
     }
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 6aaf53e80..2b8e17c22 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -612,7 +612,7 @@ docomplete(int lst)
      * no completion widgets are defined. */
 
     if (!module_loaded("zsh/compctl") && !hascompwidgets)
-	load_module("zsh/compctl");
+	(void)load_module("zsh/compctl", NULL);
 
     if (runhookdef(BEFORECOMPLETEHOOK, (void *) &lst)) {
 	active = 0;
diff --git a/Src/Zle/zleparameter.c b/Src/Zle/zleparameter.c
index 3c7f1474f..1b84fdff7 100644
--- a/Src/Zle/zleparameter.c
+++ b/Src/Zle/zleparameter.c
@@ -30,45 +30,6 @@
 #include "zleparameter.mdh"
 #include "zleparameter.pro"
 
-/* Empty dummy function for special hash parameters. */
-
-/**/
-static void
-shempty(void)
-{
-}
-
-/* Create a simple special hash parameter. */
-
-/**/
-static Param
-createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan)
-{
-    Param pm;
-    HashTable ht;
-
-    if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_REMOVABLE|PM_HASHED)))
-	return NULL;
-
-    pm->level = pm->old ? locallevel : 0;
-    pm->gsu.h = &stdhash_gsu;
-    pm->u.hash = ht = newhashtable(0, name, NULL);
-
-    ht->hash        = hasher;
-    ht->emptytable  = (TableFunc) shempty;
-    ht->filltable   = NULL;
-    ht->addnode     = (AddNodeFunc) shempty;
-    ht->getnode     = ht->getnode2 = get;
-    ht->removenode  = (RemoveNodeFunc) shempty;
-    ht->disablenode = NULL;
-    ht->enablenode  = NULL;
-    ht->freenode    = (FreeNodeFunc) shempty;
-    ht->printnode   = printparamnode;
-    ht->scantab     = scan;
-
-    return pm;
-}
-
 /* Functions for the zlewidgets special parameter. */
 
 /**/
@@ -157,18 +118,6 @@ keymapsgetfn(UNUSED(Param pm))
     return ret;
 }
 
-/* Table for defined parameters. */
-
-struct pardef {
-    char *name;
-    int flags;
-    GetNodeFunc getnfn;
-    ScanTabFunc scantfn;
-    GsuHash hash_gsu;
-    GsuArray array_gsu;
-    Param pm;
-};
-
 /*
  * This is a duplicate of stdhash_gsu.  On some systems
  * (such as Cygwin) we can't put a pointer to an imported variable
@@ -179,14 +128,18 @@ static const struct gsu_hash zlestdhash_gsu =
 static const struct gsu_array keymaps_gsu =
 { keymapsgetfn, arrsetfn, stdunsetfn };
 
-static struct pardef partab[] = {
-    { "widgets", PM_READONLY,
-      getpmwidgets, scanpmwidgets, &zlestdhash_gsu,
-      NULL, NULL },
-    { "keymaps", PM_ARRAY|PM_SPECIAL|PM_READONLY,
-      NULL, NULL, NULL,
-      &keymaps_gsu, NULL },
-    { NULL, 0, NULL, NULL, NULL, NULL, NULL }
+static struct paramdef partab[] = {
+    SPECIALPMDEF("widgets", PM_READONLY,
+		 &zlestdhash_gsu, getpmwidgets, scanpmwidgets),
+    SPECIALPMDEF("keymaps", PM_ARRAY|PM_READONLY, &keymaps_gsu, NULL, NULL),
+};
+
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+    partab, sizeof(partab)/sizeof(*partab),
+    NULL, 0,
+    0
 };
 
 /**/
@@ -198,48 +151,35 @@ setup_(UNUSED(Module m))
 
 /**/
 int
-boot_(UNUSED(Module m))
+features_(Module m, char ***features)
 {
-    struct pardef *def;
-
-    for (def = partab; def->name; def++) {
-	unsetparam(def->name);
-
-	if (def->getnfn) {
-	    if (!(def->pm = createspecialhash(def->name, def->getnfn,
-					      def->scantfn)))
-		return 1;
-	    def->pm->node.flags |= def->flags;
-	    if (def->hash_gsu)
-		def->pm->gsu.h = def->hash_gsu;
-	} else {
-	    if (!(def->pm = createparam(def->name, def->flags | PM_HIDE)))
-		return 1;
-	    def->pm->gsu.a = def->array_gsu;
-	}
-    }
+    *features = featuresarray(m->nam, &module_features);
     return 0;
 }
 
 /**/
 int
-cleanup_(UNUSED(Module m))
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m->nam, &module_features, enables);
+}
+
+/**/
+int
+boot_(UNUSED(Module m))
 {
-    Param pm;
-    struct pardef *def;
-
-    for (def = partab; def->name; def++) {
-	if ((pm = (Param) paramtab->getnode(paramtab, def->name)) &&
-	    pm == def->pm) {
-	    pm->node.flags &= ~PM_READONLY;
-	    unsetparam_pm(pm, 0, 1);
-	}
-    }
     return 0;
 }
 
 /**/
 int
+cleanup_(Module m)
+{
+    return setfeatureenables(m->nam, &module_features, NULL);
+}
+
+/**/
+int
 finish_(UNUSED(Module m))
 {
     return 0;
diff --git a/Src/builtin.c b/Src/builtin.c
index d8732af45..17af59398 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -131,7 +131,7 @@ static struct builtin builtins[] =
     BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsw", NULL),
     BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsw", "ca"),
     BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
-    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ARILabcfdipue", NULL),
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilpue", NULL),
     BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
 };
 
@@ -2033,6 +2033,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	 * because we've checked for unpleasant surprises above.
 	 */
 	pm->node.flags = (PM_TYPE(pm->node.flags) | on | PM_SPECIAL) & ~off;
+	/*
+	 * Readonlyness of special parameters must be preserved.
+	 */
+	pm->node.flags |= tpm->node.flags & PM_READONLY;
 	if (newspecial == NS_SECONDS) {
 	    /* We save off the raw internal value of the SECONDS var */
 	    tpm->u.dval = getrawseconds();
@@ -2085,7 +2089,21 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 		    "%s: array elements must be scalar", pname);
 	    return NULL;
 	}
-    } else if (isident(pname) && !idigit(*pname)) {
+    } 
+    /*
+     * As we can hide existing parameters, we allow a name if
+     * it's not a normal identifier but is one of the special
+     * set found in the parameter table.  The second test is
+     * because we can set individual positional parameters;
+     * however "0" is not a positional parameter and is OK.
+     *
+     * It would be neater to extend isident() and be clearer
+     * about where we allow various parameter types.  It's
+     * not entirely clear to me isident() should reject
+     * specially named parameters given that it accepts digits.
+     */
+    else if ((isident(pname) || paramtab->getnode(paramtab, pname))
+	     && (!idigit(*pname) || !strcmp(pname, "0"))) {
 	/*
 	 * Create a new node for a parameter with the flags in `on' minus the
 	 * readonly flag
@@ -2101,10 +2119,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 		return NULL;
 	}
     } else {
-	if (isident(pname))
-	    zerrnam(cname, "not valid in this context: %s", pname);
-	else
+	if (idigit(*pname))
 	    zerrnam(cname, "not an identifier: %s", pname);
+	else
+	    zerrnam(cname, "not valid in this context: %s", pname);
 	return NULL;
     }
 
@@ -2138,7 +2156,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 		  "BUG: parameter recreated with wrong flags");
 	    unsetparam_pm(ipm, 0, 1);
 	}
-    } else if (newspecial != NS_NONE && !(pm->old->node.flags & PM_NORESTORE)) {
+    } else if (newspecial != NS_NONE &&
+	       !(pm->old->node.flags & (PM_NORESTORE|PM_READONLY))) {
 	/*
 	 * We need to use the special setting function to re-initialise
 	 * the special parameter to empty.
diff --git a/Src/cond.c b/Src/cond.c
index 8950d845c..a597587b6 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -95,7 +95,10 @@ evalcond(Estate state, char *fromtest)
     case COND_REGEX:
 	{
 	    char *modname = isset(REMATCHPCRE) ? "zsh/pcre" : "zsh/regex";
-	    if (!load_module_silence(modname, 1)) {
+	    /*
+	     * TODO: we just need to load the appropriate condition.
+	     */
+	    if (load_module_silence(modname, NULL, 1) == 1) {
 		zwarnnam(fromtest, "%s not available for regex",
 			 modname);
 		return 2;
diff --git a/Src/exec.c b/Src/exec.c
index f711f3d30..751282127 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2012,7 +2012,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 
 		/* autoload the builtin if necessary */
 		if (!((Builtin) hn)->handlerfunc) {
-		    load_module(((Builtin) hn)->optstr);
+		    (void)load_module(((Builtin) hn)->optstr, NULL);
 		    hn = builtintab->getnode(builtintab, cmdarg);
 		}
 		assign = (hn && (hn->flags & BINF_MAGICEQUALS));
@@ -2229,7 +2229,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 
 		/* autoload the builtin if necessary */
 		if (!((Builtin) hn)->handlerfunc) {
-		    load_module(((Builtin) hn)->optstr);
+		    (void)load_module(((Builtin) hn)->optstr, NULL);
 		    hn = builtintab->getnode(builtintab, cmdarg);
 		}
 		break;
diff --git a/Src/init.c b/Src/init.c
index 6c5421e6f..fce8adcd5 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -979,7 +979,7 @@ run_init_scripts(void)
 		 * Always attempt to load the newuser module to perform
 		 * checks for new zsh users.  Don't care if we can't load it.
 		 */
-		if (load_module_silence("zsh/newuser", 1)) {
+		if (!load_module_silence("zsh/newuser", NULL, 1)) {
 		    /* Unload it immediately. */
 		    unload_named_module("zsh/newuser", "zsh", 1);
 		}
@@ -1152,7 +1152,7 @@ init_bltinmods(void)
 
 #include "bltinmods.list"
 
-    load_module("zsh/main");
+    (void)load_module("zsh/main", NULL);
 }
 
 /**/
@@ -1213,8 +1213,8 @@ char *
 autoload_zleread(char **lp, char **rp, int ha, int con)
 {
     zlereadptr = fallback_zleread;
-    if (load_module("zsh/zle"))
-	load_module("zsh/compctl");
+    if (load_module("zsh/zle", NULL) != 1)
+	(void)load_module("zsh/compctl", NULL);
     return zlereadptr(lp, rp, ha, con);
 }
 
@@ -1240,7 +1240,7 @@ static void
 autoload_zlesetkeymap(int mode)
 {
     zlesetkeymapptr = noop_function_int;
-    load_module("zsh/zle");
+    (void)load_module("zsh/zle", NULL);
     (*zlesetkeymapptr)(mode);
 }
 
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index 3af496f07..a3785cb8c 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -87,11 +87,16 @@ for bin_mod in $bin_mods; do
     echo "    {"
     echo "        extern int setup_${q_bin_mod} _((Module));"
     echo "        extern int boot_${q_bin_mod} _((Module));"
+    echo "        extern int features_${q_bin_mod} _((Module,char***));"
+    echo "        extern int enables_${q_bin_mod} _((Module,int**));"
     echo "        extern int cleanup_${q_bin_mod} _((Module));"
     echo "        extern int finish_${q_bin_mod} _((Module));"
     echo
     echo "        register_module(\"$bin_mod\","
-    echo "                        setup_${q_bin_mod}, boot_${q_bin_mod},"
+    echo "                        setup_${q_bin_mod},"
+    echo "                        features_${q_bin_mod},"
+    echo "                        enables_${q_bin_mod},"
+    echo "                        boot_${q_bin_mod},"
     echo "                        cleanup_${q_bin_mod}, finish_${q_bin_mod});"
     echo "    }"
     done_mods="$done_mods$bin_mod "
diff --git a/Src/mkmakemod.sh b/Src/mkmakemod.sh
index da01c953b..d275038d9 100644
--- a/Src/mkmakemod.sh
+++ b/Src/mkmakemod.sh
@@ -364,6 +364,8 @@ if $first_stage; then
 	echo "	    fi; \\"
 	echo "	    echo '#   define boot_ boot_${q_name}'; \\"
 	echo "	    echo '#   define cleanup_ cleanup_${q_name}'; \\"
+	echo "	    echo '#   define features_ features_${q_name}'; \\"
+	echo "	    echo '#   define enables_ enables_${q_name}'; \\"
 	echo "	    echo '#   define setup_ setup_${q_name}'; \\"
 	echo "	    echo '#   define finish_ finish_${q_name}'; \\"
 	echo "	    if test @SHORTBOOTNAMES@ = yes; then \\"
diff --git a/Src/modentry.c b/Src/modentry.c
index 87c89e0d7..ea2ca6656 100644
--- a/Src/modentry.c
+++ b/Src/modentry.c
@@ -8,7 +8,7 @@ int modentry _((int boot, Module m));
 
 /**/
 int
-modentry(int boot, Module m)
+modentry(int boot, Module m, void *ptr)
 {
     switch (boot) {
     case 0:
@@ -27,6 +27,14 @@ modentry(int boot, Module m)
 	return finish_(m);
 	break;
 
+    case 4:
+	return features_(m, (char ***)ptr);
+	break;
+
+    case 5:
+	return enables_(m, (int **)ptr);
+	break;
+
     default:
 	zerr("bad call to modentry");
 	return 1;
diff --git a/Src/module.c b/Src/module.c
index ffa659efc..8f5afca83 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -35,6 +35,20 @@
 /**/
 LinkList linkedmodules;
 
+/* $module_path ($MODULE_PATH) */
+
+/**/
+char **module_path;
+
+/* List of modules */
+
+/**/
+mod_export LinkList modules;
+
+
+/************************************************************************
+ * zsh/main standard module functions
+ ************************************************************************/
 
 /* The `zsh/main' module contains all the base code that can't actually be *
  * built as a separate module.  It is initialised by main(), so there's    *
@@ -49,6 +63,24 @@ setup_(UNUSED(Module m))
 
 /**/
 int
+features_(UNUSED(Module m), UNUSED(char ***features))
+{
+    /*
+     * There are lots and lots of features, but they're not
+     * handled here.
+     */
+    return 1;
+}
+
+/**/
+int
+enables_(UNUSED(Module m), UNUSED(int **enables))
+{
+    return 1;
+}
+
+/**/
+int
 boot_(UNUSED(Module m))
 {
     return 0;
@@ -68,12 +100,21 @@ finish_(UNUSED(Module m))
     return 0;
 }
 
+
+/************************************************************************
+ * Module utility functions
+ ************************************************************************/
+
 /* This registers a builtin module.                                   */
 
 /**/
 void
-register_module(char *n, Module_func setup, Module_func boot,
-		Module_func cleanup, Module_func finish)
+register_module(char *n, Module_void_func setup,
+		Module_features_func features,
+		Module_enables_func enables,
+		Module_void_func boot,
+		Module_void_func cleanup,
+		Module_void_func finish)
 {
     Linkedmod m;
 
@@ -81,6 +122,8 @@ register_module(char *n, Module_func setup, Module_func boot,
 
     m->name = ztrdup(n);
     m->setup = setup;
+    m->features = features;
+    m->enables = enables;
     m->boot = boot;
     m->cleanup = cleanup;
     m->finish = finish;
@@ -124,6 +167,12 @@ module_linked(char const *name)
     return NULL;
 }
 
+
+/************************************************************************
+ * Support for the various feature types.
+ * First, builtins.
+ ************************************************************************/
+
 /* addbuiltin() can be used to add a new builtin.  It returns zero on *
  * success, 1 on failure.  The only possible type of failure is that  *
  * a builtin with the specified name already exists.  An autoloaded   *
@@ -142,13 +191,81 @@ addbuiltin(Builtin b)
     return 0;
 }
 
-/* Add multiple builtins.  binl points to a table of `size' builtin      *
- * structures.  Those for which (.flags & BINF_ADDED) is false are to be *
- * added; that flag is set if they succeed.  If any fail, an error       *
- * message is printed, using nam as the leading name.  Returns 1 if all  *
- * additions succeed, 2 if some succeed and some fail, and 0 if all (and *
- * at least 1) fail.  The usual usage in a boot_*() function would be    *
- *  return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); */
+/* Define an autoloadable builtin.  It returns 0 on success, or 1 on *
+ * failure.  The only possible cause of failure is that a builtin    *
+ * with the specified name already exists.                           */
+
+/**/
+int
+add_autobin(char *nam, char *module)
+{
+    Builtin bn = zshcalloc(sizeof(*bn));
+    bn->node.nam = ztrdup(nam);
+    bn->optstr = ztrdup(module);
+    return addbuiltin(bn);
+}
+
+/* Remove the builtin added previously by addbuiltin().  Returns *
+ * zero on succes and -1 if there is no builtin with that name.  */
+
+/**/
+int
+deletebuiltin(char *nam)
+{
+    Builtin bn;
+
+    bn = (Builtin) builtintab->removenode(builtintab, nam);
+    if (!bn)
+	return -1;
+    builtintab->freenode(&bn->node);
+    return 0;
+}
+
+/**/
+mod_export int
+setbuiltins(char const *nam, Builtin binl, int size, int *e)
+{
+    int hads = 0, hadf = 0, n;
+
+    for(n = 0; n < size; n++) {
+	Builtin b = &binl[n];
+	if (e && *e++) {
+	    if (b->node.flags & BINF_ADDED)
+		continue;
+	    if (addbuiltin(b)) {
+		zwarnnam(nam,
+			 "name clash when adding builtin `%s'", b->node.nam);
+		hadf = 1;
+	    } else {
+		b->node.flags |= BINF_ADDED;
+		hads = 2;
+	    }
+	} else {
+	    if (!(b->node.flags & BINF_ADDED))
+		continue;
+	    if (deletebuiltin(b->node.nam)) {
+		zwarnnam(nam, "builtin `%s' already deleted", b->node.nam);
+		hadf = 1;
+	    } else {
+		hads = 2;
+		b->node.flags &= ~BINF_ADDED;
+	    }
+	}
+    }
+    return hadf ? hads : 1;
+}
+
+/*
+ * Add multiple builtins.  binl points to a table of `size' builtin
+ * structures.  Those for which (.flags & BINF_ADDED) is false are to be
+ * added; that flag is set if they succeed.
+ *
+ * If any fail, an error message is printed, using nam as the leading name.
+ * Returns 1 if all additions succeed, 2 if some succeed and some fail, and 0
+ * if all (and at least 1) fail.
+ *
+ * This should not be used from a module; instead, use handlefeatures().
+ */
 
 /**/
 mod_export int
@@ -171,6 +288,11 @@ addbuiltins(char const *nam, Builtin binl, int size)
     return hadf ? hads : 1;
 }
 
+
+/************************************************************************
+ * Function wrappers.
+ ************************************************************************/
+
 /* The list of function wrappers defined. */
 
 /**/
@@ -208,104 +330,713 @@ addwrapper(Module m, FuncWrap w)
     return 0;
 }
 
-/* $module_path ($MODULE_PATH) */
+/* This removes the given wrapper definition from the list. Returned is *
+ * one in case of error and zero otherwise. */
 
 /**/
-char **module_path;
+mod_export int
+deletewrapper(Module m, FuncWrap w)
+{
+    FuncWrap p, q;
 
-/* List of modules */
+    if (m->flags & MOD_ALIAS)
+	return 1;
+
+    if (w->flags & WRAPF_ADDED) {
+	for (p = wrappers, q = NULL; p && p != w; q = p, p = p->next);
+
+	if (p) {
+	    if (q)
+		q->next = p->next;
+	    else
+		wrappers = p->next;
+	    p->flags &= ~WRAPF_ADDED;
+
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+
+/************************************************************************
+ * Conditions.
+ ************************************************************************/
+
+/* The list of module-defined conditions. */
 
 /**/
-mod_export LinkList modules;
+mod_export Conddef condtab;
 
-/* Define an autoloadable builtin.  It returns 0 on success, or 1 on *
- * failure.  The only possible cause of failure is that a builtin    *
- * with the specified name already exists.                           */
+/* This gets a condition definition with the given name. The first        *
+ * argument says if we have to look for an infix condition. The last      *
+ * argument is non-zero if we should autoload modules if needed. */
+
+/**/
+Conddef
+getconddef(int inf, char *name, int autol)
+{
+    Conddef p;
+    int f = 1;
+
+    do {
+	for (p = condtab; p; p = p->next) {
+	    if ((!!inf == !!(p->flags & CONDF_INFIX)) &&
+		!strcmp(name, p->name))
+		break;
+	}
+	if (autol && p && p->module) {
+	    /* This is a definition for an autoloaded condition, load the *
+	     * module if we haven't tried that already. */
+	    if (f) {
+		(void)load_module_silence(p->module, NULL, 0);
+		f = 0;
+		p = NULL;
+	    } else {
+		deleteconddef(p);
+		return NULL;
+	    }
+	} else
+	    break;
+    } while (!p);
+    return p;
+}
+
+/*
+ * This adds the given condition definition. The return value is zero on *
+ * success and 1 on failure. If there is a matching definition for an    *
+ * autoloaded condition, it is removed.
+ *
+ * This is used for adding both an autoload definition or
+ * a real condition.  In the latter case the caller is responsible
+ * for setting the CONDF_ADDED flag.
+ */
+
+/**/
+static int
+addconddef(Conddef c)
+{
+    Conddef p = getconddef((c->flags & CONDF_INFIX), c->name, 0);
+
+    if (p) {
+	if (!p->module || (p->flags & CONDF_ADDED))
+	    return 1;
+	/* There is an autoload definition. */
+
+	deleteconddef(p);
+    }
+    c->next = condtab;
+    condtab = c;
+    return 0;
+}
+
+/* This removes the given condition definition from the list(s). If this *
+ * is a definition for a autoloaded condition, the memory is freed. */
 
 /**/
 int
-add_autobin(char *nam, char *module)
+deleteconddef(Conddef c)
 {
-    Builtin bn = zshcalloc(sizeof(*bn));
-    bn->node.nam = ztrdup(nam);
-    bn->optstr = ztrdup(module);
-    return addbuiltin(bn);
+    Conddef p, q;
+
+    for (p = condtab, q = NULL; p && p != c; q = p, p = p->next);
+
+    if (p) {
+	if (q)
+	    q->next = p->next;
+	else 
+	    condtab = p->next;
+		
+	if (p->module) {
+	    /* autoloaded, free it */
+	    zsfree(p->name);
+	    zsfree(p->module);
+	    zfree(p, sizeof(*p));
+	}
+	return 0;
+    }
+    return -1;
 }
 
-/* Remove the builtin added previously by addbuiltin().  Returns *
- * zero on succes and -1 if there is no builtin with that name.  */
+/**/
+mod_export int
+setconddefs(char const *nam, Conddef c, int size, int *e)
+{
+    int hads = 0, hadf = 0;
+
+    while (size--) {
+	if (e && *e++) {
+	    if (c->flags & CONDF_ADDED) {
+		c++;
+		continue;
+	    }
+	    if (addconddef(c)) {
+		zwarnnam(nam, "name clash when adding condition `%s'",
+			 c->name);
+		hadf = 1;
+	    } else {
+		c->flags |= CONDF_ADDED;
+		hads = 2;
+	    }
+	} else {
+	    if (!(c->flags & CONDF_ADDED)) {
+		c++;
+		continue;
+	    }
+	    if (deleteconddef(c)) {
+		zwarnnam(nam, "condition `%s' already deleted", c->name);
+		hadf = 1;
+	    } else {
+		c->flags &= ~CONDF_ADDED;
+		hads = 2;
+	    }
+	}
+	c++;
+    }
+    return hadf ? hads : 1;
+}
+
+/* This adds a definition for autoloading a module for a condition. */
 
 /**/
 int
-deletebuiltin(char *nam)
+add_autocond(char *nam, int inf, char *module)
 {
-    Builtin bn;
+    Conddef c = (Conddef) zalloc(sizeof(*c));
 
-    bn = (Builtin) builtintab->removenode(builtintab, nam);
-    if (!bn)
-	return -1;
-    builtintab->freenode(&bn->node);
+    c->name = ztrdup(nam);
+    c->flags = (inf  ? CONDF_INFIX : 0);
+    c->module = ztrdup(module);
+
+    if (addconddef(c)) {
+	zsfree(c->name);
+	zsfree(c->module);
+	zfree(c, sizeof(*c));
+
+	return 1;
+    }
     return 0;
 }
 
-/* Delete multiple builtins.  binl points to a table of `size' builtin  *
- * structures.  Those for which (.flags & BINF_ADDED) is true are to be *
- * deleted; that flag is cleared.  If any fail, an error message is     *
- * printed, using nam as the leading name.  Returns 1 if all deletions  *
- * succeed, 2 if some succeed and some fail, and 0 if all (and at least *
- * 1) fail.  In normal use, from a cleanup_*() function, this return    *
- * value would be ignored -- the only cause of failure would be that a  *
- * wayward module had deleted our builtin without telling us.           */
+
+/************************************************************************
+ * Hook functions.
+ ************************************************************************/
+
+/* This list of hook functions defined. */
+
+/**/
+Hookdef hooktab;
+
+/* Find a hook definition given the name. */
+
+/**/
+Hookdef
+gethookdef(char *n)
+{
+    Hookdef p;
+
+    for (p = hooktab; p; p = p->next)
+	if (!strcmp(n, p->name))
+	    return p;
+    return NULL;
+}
+
+/* This adds the given hook definition. The return value is zero on      *
+ * success and 1 on failure.                                             */
+
+/**/
+int
+addhookdef(Hookdef h)
+{
+    if (gethookdef(h->name))
+	return 1;
+
+    h->next = hooktab;
+    hooktab = h;
+    h->funcs = znewlinklist();
+
+    return 0;
+}
+
+/* This adds multiple hook definitions. This is like addbuiltins(). */
 
 /**/
 mod_export int
-deletebuiltins(char const *nam, Builtin binl, int size)
+addhookdefs(char const *nam, Hookdef h, int size)
 {
-    int hads = 0, hadf = 0, n;
+    int hads = 0, hadf = 0;
 
-    for(n = 0; n < size; n++) {
-	Builtin b = &binl[n];
-	if(!(b->node.flags & BINF_ADDED))
-	    continue;
-	if(deletebuiltin(b->node.nam)) {
-	    zwarnnam(nam, "builtin `%s' already deleted", b->node.nam);
+    while (size--) {
+	if (addhookdef(h)) {
+	    zwarnnam(nam, "name clash when adding hook `%s'", h->name);
 	    hadf = 1;
 	} else
 	    hads = 2;
-	b->node.flags &= ~BINF_ADDED;
+	h++;
     }
     return hadf ? hads : 1;
 }
 
-/* This removes the given wrapper definition from the list. Returned is *
- * one in case of error and zero otherwise. */
+/* Delete hook definitions. */
 
 /**/
-mod_export int
-deletewrapper(Module m, FuncWrap w)
+int
+deletehookdef(Hookdef h)
 {
-    FuncWrap p, q;
+    Hookdef p, q;
 
-    if (m->flags & MOD_ALIAS)
+    for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next);
+
+    if (!p)
 	return 1;
 
-    if (w->flags & WRAPF_ADDED) {
-	for (p = wrappers, q = NULL; p && p != w; q = p, p = p->next);
+    if (q)
+	q->next = p->next;
+    else
+	hooktab = p->next;
+    freelinklist(p->funcs, NULL);
+    return 0;
+}
 
-	if (p) {
-	    if (q)
-		q->next = p->next;
-	    else
-		wrappers = p->next;
-	    p->flags &= ~WRAPF_ADDED;
+/**/
+mod_export int
+deletehookdefs(UNUSED(char const *nam), Hookdef h, int size)
+{
+    while (size--) {
+	deletehookdef(h);
+	h++;
+    }
+    return 1;
+}
 
+/* Add a function to a hook. */
+
+/**/
+int
+addhookdeffunc(Hookdef h, Hookfn f)
+{
+    zaddlinknode(h->funcs, (void *) f);
+
+    return 0;
+}
+
+/**/
+mod_export int
+addhookfunc(char *n, Hookfn f)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return addhookdeffunc(h, f);
+    return 1;
+}
+
+/* Delete a function from a hook. */
+
+/**/
+int
+deletehookdeffunc(Hookdef h, Hookfn f)
+{
+    LinkNode p;
+
+    for (p = firstnode(h->funcs); p; incnode(p))
+	if (f == (Hookfn) getdata(p)) {
+	    remnode(h->funcs, p);
 	    return 0;
 	}
-    }
     return 1;
 }
 
 /**/
+mod_export int
+deletehookfunc(char *n, Hookfn f)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return deletehookdeffunc(h, f);
+    return 1;
+}
+
+/* Run the function(s) for a hook. */
+
+/**/
+mod_export int
+runhookdef(Hookdef h, void *d)
+{
+    if (empty(h->funcs)) {
+	if (h->def)
+	    return h->def(h, d);
+	return 0;
+    } else if (h->flags & HOOKF_ALL) {
+	LinkNode p;
+	int r;
+
+	for (p = firstnode(h->funcs); p; incnode(p))
+	    if ((r = ((Hookfn) getdata(p))(h, d)))
+		return r;
+	if (h->def)
+	    return h->def(h, d);
+	return 0;
+    } else
+	return ((Hookfn) getdata(lastnode(h->funcs)))(h, d);
+}
+
+/**/
+int
+runhook(char *n, void *d)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return runhookdef(h, d);
+    return 0;
+}
+
+
+/************************************************************************
+ * Shell parameters.
+ ************************************************************************/
+
+static int
+checkaddparam(char *nam)
+{
+    Param pm;
+
+    if (!(pm = (Param) gethashnode2(paramtab, nam)))
+	return 0;
+
+    if (pm->level || !(pm->node.flags & PM_AUTOLOAD)) {
+	zwarn("Can't add module parameter `%s': %s",
+	      nam, pm->level ?
+	      "local parameter exists" :
+	      "parameter already exists");
+	return 1;
+    }
+
+    unsetparam_pm(pm, 0, 1);
+    return 0;
+}
+
+/* This adds the given parameter definition. The return value is zero on *
+ * success and 1 on failure. */
+
+/**/
+int
+addparamdef(Paramdef d)
+{
+    Param pm;
+
+    if (checkaddparam(d->name))
+	return 1;
+
+    if (d->getnfn) {
+	if (!(pm = createspecialhash(d->name, d->getnfn,
+				     d->scantfn, d->flags)))
+	    return 1;
+    }
+    else if (!(pm = createparam(d->name, d->flags)) &&
+	!(pm = (Param) paramtab->getnode(paramtab, d->name)))
+	return 1;
+
+    d->pm = pm;
+    pm->level = 0;
+    if (d->var)
+	pm->u.data = d->var;
+    if (d->var || d->gsu) {
+	/*
+	 * If no get/set/unset class, use the appropriate
+	 * variable type, else use the one supplied.
+	 */
+	switch (PM_TYPE(pm->node.flags)) {
+	case PM_SCALAR:
+	    pm->gsu.s = d->gsu ? (GsuScalar)d->gsu : &varscalar_gsu;
+	    break;
+
+	case PM_INTEGER:
+	    pm->gsu.i = d->gsu ? (GsuInteger)d->gsu : &varinteger_gsu;
+	    break;
+
+	case PM_ARRAY:
+	    pm->gsu.a = d->gsu ? (GsuArray)d->gsu : &vararray_gsu;
+	    break;
+
+	case PM_HASHED:
+	    /* hashes may behave like standard hashes */
+	    if (d->gsu)
+		pm->gsu.h = (GsuHash)d->gsu;
+	    break;
+
+	default:
+	    unsetparam_pm(pm, 0, 1);
+	    return 1;
+	}
+    }
+
+    return 0;
+}
+
+/* Delete parameters defined. No error checking yet. */
+
+/**/
+int
+deleteparamdef(Paramdef d)
+{
+    Param pm = (Param) paramtab->getnode(paramtab, d->name);
+
+    if (!pm)
+	return 1;
+    if (pm != d->pm) {
+	/*
+	 * See if the parameter has been hidden.  If so,
+	 * bring it to the front to unset it.
+	 */
+	Param prevpm, searchpm;
+	for (prevpm = pm, searchpm = pm->old;
+	     searchpm;
+	     prevpm = searchpm, searchpm = searchpm->old)
+	    if (searchpm == d->pm)
+		break;
+
+	if (!searchpm)
+	    return 1;
+
+	paramtab->removenode(paramtab, pm->node.nam);
+	prevpm->old = searchpm->old;
+	searchpm->old = pm;
+	paramtab->addnode(paramtab, searchpm->node.nam, searchpm);
+
+	pm = searchpm;
+    }
+    pm->node.flags = (pm->node.flags & ~PM_READONLY) | PM_REMOVABLE;
+    unsetparam_pm(pm, 0, 1);
+    d->pm = NULL;
+    return 0;
+}
+
+/**/
+mod_export int
+setparamdefs(char const *nam, Paramdef d, int size, int *e)
+{
+    int hads = 0, hadf = 0;
+
+    while (size--) {
+	if (e && *e++) {
+	    if (d->pm) {
+		d++;
+		continue;
+	    }
+	    if (addparamdef(d)) {
+		zwarnnam(nam, "error when adding parameter `%s'", d->name);
+		hadf = 1;
+	    } else {
+		hads = 2;
+	    }
+	} else {
+	    if (!d->pm) {
+		d++;
+		continue;
+	    }
+	    if (deleteparamdef(d)) {
+		zwarnnam(nam, "parameter `%s' already deleted", d->name);
+		hadf = 1;
+	    } else {
+		hads = 2;
+	    }
+	}
+	d++;
+    }
+    return hadf ? hads : 1;
+}
+
+/* This adds a definition for autoloading a module for a parameter. */
+
+/**/
+void
+add_autoparam(char *nam, char *module)
+{
+    Param pm;
+
+    queue_signals();
+    if (checkaddparam(nam)) {
+	unqueue_signals();
+	return;
+    }
+
+    pm = setsparam(nam, ztrdup(module));
+
+    pm->node.flags |= PM_AUTOLOAD;
+    unqueue_signals();
+}
+
+
+/************************************************************************
+ * Math functions.
+ ************************************************************************/
+
+/* List of math functions. */
+
+/**/
+MathFunc mathfuncs;
+
+/**/
+void
+removemathfunc(MathFunc previous, MathFunc current)
+{
+    if (previous)
+	previous->next = current->next;
+    else
+	mathfuncs = current->next;
+
+    zsfree(current->name);
+    zsfree(current->module);
+    zfree(current, sizeof(*current));
+}
+
+/**/
+MathFunc
+getmathfunc(char *name, int autol)
+{
+    MathFunc p, q = NULL;
+
+    for (p = mathfuncs; p; q = p, p = p->next)
+	if (!strcmp(name, p->name)) {
+	    if (autol && p->module && !(p->flags & MFF_USERFUNC)) {
+		char *n = dupstring(p->module);
+
+		removemathfunc(q, p);
+
+		(void)load_module_silence(n, NULL, 0);
+
+		return getmathfunc(name, 0);
+	    }
+	    return p;
+	}
+
+    return NULL;
+}
+
+/**/
+static int
+addmathfunc(MathFunc f)
+{
+    MathFunc p, q = NULL;
+
+    if (f->flags & MFF_ADDED)
+	return 1;
+
+    for (p = mathfuncs; p; q = p, p = p->next)
+	if (!strcmp(f->name, p->name)) {
+	    if (p->module && !(p->flags & MFF_USERFUNC)) {
+		/*
+		 * Autoloadable, replace.
+		 */
+		removemathfunc(q, p);
+		break;
+	    }
+	    return 1;
+	}
+
+    f->next = mathfuncs;
+    mathfuncs = f;
+
+    return 0;
+}
+
+/**/
+mod_export int
+deletemathfunc(MathFunc f)
+{
+    MathFunc p, q;
+
+    for (p = mathfuncs, q = NULL; p && p != f; q = p, p = p->next);
+
+    if (p) {
+	if (q)
+	    q->next = f->next;
+	else
+	    mathfuncs = f->next;
+
+	/* the following applies to both unloaded and user-defined functions */
+	if (f->module) {
+	    zsfree(f->name);
+	    zsfree(f->module);
+	    zfree(f, sizeof(*f));
+	} else
+	    f->flags &= ~MFF_ADDED;
+
+	return 0;
+    }
+    return -1;
+}
+
+/**/
+mod_export int
+setmathfuncs(char const *nam, MathFunc f, int size, int *e)
+{
+    int hads = 0, hadf = 0;
+
+    while (size--) {
+	if (e && *e++) {
+	    if (f->flags & MFF_ADDED) {
+		f++;
+		continue;
+	    }
+	    if (addmathfunc(f)) {
+		zwarnnam(nam, "name clash when adding math function `%s'",
+			 f->name);
+		hadf = 1;
+	    } else {
+		f->flags |= MFF_ADDED;
+		hads = 2;
+	    }
+	} else {
+	    if (!(f->flags & MFF_ADDED)) {
+		f++;
+		continue;
+	    }
+	    if (deletemathfunc(f)) {
+		zwarnnam(nam, "math function `%s' already deleted", f->name);
+		hadf = 1;
+	    } else {
+		f->flags &= ~MFF_ADDED;
+		hads = 2;
+	    }	    
+	}
+	f++;
+    }
+    return hadf ? hads : 1;
+}
+
+/**/
+int
+add_automathfunc(char *nam, char *module)
+{
+    MathFunc f = (MathFunc) zalloc(sizeof(*f));
+
+    f->name = ztrdup(nam);
+    f->module = ztrdup(module);
+    f->flags = 0;
+
+    if (addmathfunc(f)) {
+	zsfree(f->name);
+	zsfree(f->module);
+	zfree(f, sizeof(*f));
+
+	return 1;
+    }
+
+    return 0;
+}
+
+
+/************************************************************************
+ * Now support for dynamical loading and the fallback functions
+ * we use for loading if dynamical loading is not available.
+ ************************************************************************/
+
+/**/
 #ifdef DYNAMIC
 
 /**/
@@ -392,11 +1123,15 @@ hpux_dlsym(void *handle, char *name)
 
 #ifdef DLSYM_NEEDS_UNDERSCORE
 # define STR_SETUP     "_setup_"
+# define STR_FEATURES  "_features_"
+# define STR_ENABLES   "_enables_"
 # define STR_BOOT      "_boot_"
 # define STR_CLEANUP   "_cleanup_"
 # define STR_FINISH    "_finish_"
 #else /* !DLSYM_NEEDS_UNDERSCORE */
 # define STR_SETUP     "setup_"
+# define STR_FEATURES  "features_"
+# define STR_ENABLES   "enables_"
 # define STR_BOOT      "boot_"
 # define STR_CLEANUP   "cleanup_"
 # define STR_FINISH    "finish_"
@@ -542,38 +1277,52 @@ module_loaded(const char *name)
 static int
 dyn_setup_module(Module m)
 {
-    return ((int (*)_((int,Module))) m->u.handle)(0, m);
+    return ((int (*)_((int,Module))) m->u.handle)(0, m, NULL);
+}
+
+/**/
+static int
+dyn_features_module(Module m, char ***features)
+{
+    return ((int (*)_((int,Module))) m->u.handle)(4, m, features);
+}
+
+/**/
+static int
+dyn_enables_module(Module m, int **enables)
+{
+    return ((int (*)_((int,Module))) m->u.handle)(5, m, enables);
 }
 
 /**/
 static int
 dyn_boot_module(Module m)
 {
-    return ((int (*)_((int,Module))) m->u.handle)(1, m);
+    return ((int (*)_((int,Module))) m->u.handle)(1, m, NULL);
 }
 
 /**/
 static int
 dyn_cleanup_module(Module m)
 {
-    return ((int (*)_((int,Module))) m->u.handle)(2, m);
+    return ((int (*)_((int,Module))) m->u.handle)(2, m, NULL);
 }
 
 /**/
 static int
 dyn_finish_module(Module m)
 {
-    return ((int (*)_((int,Module))) m->u.handle)(3, m);
+    return ((int (*)_((int,Module))) m->u.handle)(3, m, NULL);
 }
 
 /**/
 #else
 
-static Module_func
+static Module_generic_func
 module_func(Module m, char *name)
 {
 #ifdef DYNAMIC_NAME_CLASH_OK
-    return (Module_func) dlsym(m->u.handle, name);
+    return (Module_generic_func) dlsym(m->u.handle, name);
 #else /* !DYNAMIC_NAME_CLASH_OK */
     VARARR(char, buf, strlen(name) + strlen(m->nam)*2 + 1);
     char const *p;
@@ -594,7 +1343,7 @@ module_func(Module m, char *name)
 	    *q++ = *p;
     }
     *q = 0;
-    return (Module_func) dlsym(m->u.handle, buf);
+    return (Module_generic_func) dlsym(m->u.handle, buf);
 #endif /* !DYNAMIC_NAME_CLASH_OK */
 }
 
@@ -602,7 +1351,7 @@ module_func(Module m, char *name)
 static int
 dyn_setup_module(Module m)
 {
-    Module_func fn = module_func(m, STR_SETUP);
+    Module_void_func fn = (Module_void_func)module_func(m, STR_SETUP);
 
     if (fn)
 	return fn(m);
@@ -612,9 +1361,34 @@ dyn_setup_module(Module m)
 
 /**/
 static int
+dyn_features_module(Module m, char ***features)
+{
+    Module_features_func fn =
+	(Module_features_func)module_func(m, STR_FEATURES);
+
+    if (fn)
+	return fn(m, features);
+    /* not a user-visible error if no features function */
+    return 1;
+}
+
+/**/
+static int
+dyn_enables_module(Module m, int **enables)
+{
+    Module_enables_func fn = (Module_enables_func)module_func(m, STR_ENABLES);
+
+    if (fn)
+	return fn(m, enables);
+    /* not a user-visible error if no enables function */
+    return 1;
+}
+
+/**/
+static int
 dyn_boot_module(Module m)
 {
-    Module_func fn = module_func(m, STR_BOOT);
+    Module_void_func fn = (Module_void_func)module_func(m, STR_BOOT);
 
     if(fn)
 	return fn(m);
@@ -626,7 +1400,7 @@ dyn_boot_module(Module m)
 static int
 dyn_cleanup_module(Module m)
 {
-    Module_func fn = module_func(m, STR_CLEANUP);
+    Module_void_func fn = (Module_void_func)module_func(m, STR_CLEANUP);
 
     if(fn)
 	return fn(m);
@@ -641,7 +1415,7 @@ dyn_cleanup_module(Module m)
 static int
 dyn_finish_module(Module m)
 {
-    Module_func fn = module_func(m, STR_FINISH);
+    Module_void_func fn = (Module_void_func)module_func(m, STR_FINISH);
     int r;
 
     if (fn)
@@ -667,6 +1441,24 @@ setup_module(Module m)
 
 /**/
 static int
+features_module(Module m, char ***features)
+{
+    return ((m->flags & MOD_LINKED) ?
+	    (m->u.linked->features)(m, features) :
+	    dyn_features_module(m, features));
+}
+
+/**/
+static int
+enables_module(Module m, int **enables)
+{
+    return ((m->flags & MOD_LINKED) ?
+	    (m->u.linked->enables)(m, enables) :
+	    dyn_enables_module(m, enables));
+}
+
+/**/
+static int
 boot_module(Module m)
 {
     return ((m->flags & MOD_LINKED) ?
@@ -701,6 +1493,22 @@ setup_module(Module m)
 
 /**/
 static int
+features_module(Module m, char ***features)
+{
+    return ((m->flags & MOD_LINKED) ? (m->u.linked->features)(m, features)
+	    : 1);
+}
+
+/**/
+static int
+enables_module(Module m, int **enables)
+{
+    return ((m->flags & MOD_LINKED) ? (m->u.linked->enables)(m, enables)
+	    : 1);
+}
+
+/**/
+static int
 boot_module(Module m)
 {
     return ((m->flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1);
@@ -723,6 +1531,125 @@ finish_module(Module m)
 /**/
 #endif /* !DYNAMIC */
 
+
+/************************************************************************
+ * Functions called when manipulating modules
+ ************************************************************************/
+
+/*
+ * Set the features for the module, which must be loaded
+ * by now (though may not be fully set up).
+ *
+ * Return 0 for success, 1 for failure, 2 if some features
+ * couldn't be set.
+ */
+
+/**/
+static int
+do_module_features(Module m, char **enablesstr, int silent)
+{
+    char **features;
+
+    if (features_module(m, &features) == 0) {
+	/*
+	 * Features are supported.  If we were passed
+	 * a NULL array, enable all features, else
+	 * enable only the features listed.
+	 * (This may in principle be an empty array,
+	 * although that's not very pointful.)
+	 */
+	int *enables = NULL;
+	if (enables_module(m, &enables)) {
+	    /* If features are supported, enables should be, too */
+	    if (!silent)
+		zwarn("error getting enabled features for module `%s'",
+		      m->nam);
+	    return 1;
+	}
+
+	if (enablesstr) {
+	    char **ep;
+	    for (ep = enablesstr; *ep; ep++) {
+		char **fp, *esp = *ep;
+		int on = 1;
+		if (*esp == '+')
+		    esp++;
+		else if (*esp == '-') {
+		    on = 0;
+		    esp++;
+		}
+		for (fp = features; *fp; fp++)
+		    if (!strcmp(*fp, esp)) {
+			enables[fp - features] = on;
+			break;
+		    }
+		if (!*fp) {
+		    if (!silent)
+			zwarn("module `%s' has no such feature: %s",
+			      m->nam, esp);
+		    return 1;
+		}
+	    }
+	} else {
+	    /*
+	     * Enable all features.  This is used when loading
+	     * without using zmodload -F.
+	     */
+	    int n_features = arrlen(features);
+	    int *ep;
+	    for (ep = enables; n_features--; ep++)
+		*ep = 1;
+	}
+
+	if (enables_module(m, &enables))
+	    return 2;
+    } else if (enablesstr) {
+	if (!silent)
+	    zwarn("module `%s' does not support features", m->nam);
+	return 1;
+    }
+    /* Else it doesn't support features but we don't care. */
+
+    return 0;
+}
+
+/*
+ * Boot the module, including setting up features.
+ * As we've only just loaded the module, we don't yet
+ * know what features it supports, so we get them passed
+ * as a string.
+ *
+ * Returns 0 if OK, 1 if completely failed, 2 if some features
+ * couldn't be set up.
+ */
+
+/**/
+static int
+do_boot_module(Module m, char **enablesstr, int silent)
+{
+    int ret = do_module_features(m, enablesstr, silent);
+
+    if (ret == 1)
+	return 1;
+
+    if (boot_module(m))
+	return 1;
+    return ret;
+}
+
+/*
+ * Cleanup the module.
+ */
+
+/**/
+static int
+do_cleanup_module(Module m)
+{
+    return (m->flags & MOD_LINKED) ?
+	(m->u.linked && m->u.linked->cleanup(m)) :
+	(m->u.handle && cleanup_module(m));
+}
+
 /**/
 static int
 modname_ok(char const *p)
@@ -735,27 +1662,37 @@ modname_ok(char const *p)
     return 0;
 }
 
+/*
+ * Now returns 0 for success (changed post-4.3.4),
+ * 1 for complete failure, 2 if some features couldn't be set.
+ */
+
 /**/
 mod_export int
-load_module(char const *name)
+load_module(char const *name, char **enablesstr)
 {
-    return load_module_silence(name, 0);
+    return load_module_silence(name, enablesstr, 0);
 }
 
+/*
+ * Returns 0 for success (changed post-4.3.4), 1 for complete
+ * failure, 2 if some features couldn't be set.
+ */
+
 /**/
 mod_export int
-load_module_silence(char const *name, int silent)
+load_module_silence(char const *name, char **enablesstr, int silent)
 {
     Module m;
     void *handle = NULL;
     Linkedmod linked;
     LinkNode node, n;
-    int set;
+    int set, bootret;
 
     if (!modname_ok(name)) {
 	if (!silent)
 	    zerr("invalid module name `%s'", name);
-	return 0;
+	return 1;
     }
     /*
      * The following function call may alter name to the final name in a
@@ -767,7 +1704,7 @@ load_module_silence(char const *name, int silent)
 	if (!(linked = module_linked(name)) &&
 	    !(handle = do_load_module(name, silent))) {
 	    unqueue_signals();
-	    return 0;
+	    return 1;
 	}
 	m = zshcalloc(sizeof(*m));
 	m->nam = ztrdup(name);
@@ -780,40 +1717,47 @@ load_module_silence(char const *name, int silent)
 	}
 	node = zaddlinknode(modules, m);
 
-	if ((set = setup_module(m)) || boot_module(m)) {
-	    if (!set)
+	if ((set = setup_module(m)) ||
+	    (bootret = do_boot_module(m, enablesstr, silent)) == 1) {
+	    if (!set) {
+		do_cleanup_module(m);
 		finish_module(m);
+	    }
 	    delete_module(node);
 	    unqueue_signals();
-	    return 0;
+	    return 1;
 	}
 	m->flags |= MOD_INIT_S | MOD_INIT_B;
 	m->flags &= ~MOD_SETUP;
 	unqueue_signals();
-	return 1;
-    } 
+	return bootret;
+    }
     m = (Module) getdata(node);
     if (m->flags & MOD_SETUP) {
 	unqueue_signals();
-	return 1;
+	return 0;
     }
     if (m->flags & MOD_UNLOAD)
 	m->flags &= ~MOD_UNLOAD;
     else if ((m->flags & MOD_LINKED) ? m->u.linked : m->u.handle) {
 	unqueue_signals();
-	return 1;
+	return 0;
     }
     if (m->flags & MOD_BUSY) {
 	zerr("circular dependencies for module %s", name);
-	return 0;
+	return 1;
     }
     m->flags |= MOD_BUSY;
+    /*
+     * TODO: shouldn't we unload the module if one of
+     * its dependencies fails?
+     */
     if (m->deps)
 	for (n = firstnode(m->deps); n; incnode(n))
-	    if (!load_module_silence((char *) getdata(n), silent)) {
+	    if (load_module_silence((char *) getdata(n), NULL, silent) == 1) {
 		m->flags &= ~MOD_BUSY;
 		unqueue_signals();
-		return 0;
+		return 1;
 	    }
     m->flags &= ~MOD_BUSY;
     if (!m->u.handle) {
@@ -821,7 +1765,7 @@ load_module_silence(char const *name, int silent)
 	if (!(linked = module_linked(name)) &&
 	    !(handle = do_load_module(name, silent))) {
 	    unqueue_signals();
-	    return 0;
+	    return 1;
 	}
 	if (handle) {
 	    m->u.handle = handle;
@@ -837,12 +1781,13 @@ load_module_silence(char const *name, int silent)
 		m->u.linked = NULL;
 	    m->flags &= ~MOD_SETUP;
 	    unqueue_signals();
-	    return 0;
+	    return 1;
 	}
 	m->flags |= MOD_INIT_S;
     }
     m->flags |= MOD_SETUP;
-    if (boot_module(m)) {
+    if ((bootret = do_boot_module(m, enablesstr, silent)) == 1) {
+	do_cleanup_module(m);
 	finish_module(m);
 	if (m->flags & MOD_LINKED)
 	    m->u.linked = NULL;
@@ -850,42 +1795,44 @@ load_module_silence(char const *name, int silent)
 	    m->u.handle = NULL;
 	m->flags &= ~MOD_SETUP;
 	unqueue_signals();
-	return 0;
+	return 1;
     }
     m->flags |= MOD_INIT_B;
     m->flags &= ~MOD_SETUP;
     unqueue_signals();
-    return 1;
+    return bootret;
 }
 
 /* This ensures that the module with the name given as the second argument
  * is loaded.
- * The third argument should be non-zero if the function should complain
- * about trying to load a module with a full path name in restricted mode.
- * The last argument should be non-zero if this function should signal an
- * error if the module is already loaded.
- * The return value is non-zero if the module was found or loaded. */
+ * The last argument is the array of features to set.  If this is NULL
+ * and the module needs to be loaded, all features are enabled.
+ * If this is non-NULL the module features are set accordingly
+ * whether or not the module is loaded; it is an error if the
+ * module does not support the features passed (even if the feature
+ * is to be turned off) or if the module does not support features
+ * at all.
+ * The return value is 0 if the module was found or loaded
+ * (this changed post-4.3.4, because I got so confused---pws),
+ * 1 if loading failed completely, 2 if some features couldn't be set.
+ */
 
 /**/
 mod_export int
-require_module(char *nam, const char *module, UNUSED(int res), int test)
+require_module(char *nam, const char *module, char **features)
 {
     Module m = NULL;
     LinkNode node;
-    int ret = 1;
+    int ret = 0;
 
     /* Resolve aliases and actual loadable module as for load_module */
     queue_signals();
     node = find_module(module, 1, &module);
-    if (node && (m = ((Module) getdata(node)))->u.handle &&
-	!(m->flags & MOD_UNLOAD)) {
-	if (test) {
-	    unqueue_signals();
-	    zwarnnam(nam, "module %s already loaded.", module);
-	    return 0;
-	}
-    } else
-	ret = load_module_silence(module, 0);
+    if (!node || !(m = ((Module) getdata(node)))->u.handle ||
+	(m->flags & MOD_UNLOAD))
+	ret = load_module_silence(module, features, 0);
+    else
+	ret = do_module_features(m, features, 0);
     unqueue_signals();
 
     return ret;
@@ -953,6 +1900,11 @@ autoloadscan(HashNode hn, int printflags)
     putchar('\n');
 }
 
+
+/************************************************************************
+ * Handling for the zmodload builtin and its various options.
+ ************************************************************************/
+
 /**/
 int
 bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
@@ -961,10 +1913,18 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
 	OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f');
     int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u');
     int ret = 1;
+    /* options only allowed with -F */
+    char *fonly = "lP", *fp;
 
-    if (ops_bcpf && !ops_au) {
-	zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u");
-	return 1;
+    if (ops_bcpf) {
+	if (!ops_au) {
+	    zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u");
+	    return 1;
+	}
+	if (OPT_ISSET(ops,'F')) {
+	    zwarnnam(nam, "-b, -c, -f, and -p cannot be combined with -F");
+	    return 1;
+	}
     }
     if (OPT_ISSET(ops,'A') || OPT_ISSET(ops,'R')) {
 	if (ops_bcpf || ops_au || OPT_ISSET(ops,'d') || 
@@ -987,10 +1947,19 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
 			       OPT_ISSET(ops,'a') || OPT_ISSET(ops,'d') ||
 			       OPT_ISSET(ops,'i') || OPT_ISSET(ops,'u'))) {
 	zwarnnam(nam, "-e cannot be combined with other options");
+	/* except -F ... */
 	return 1;
     }
+    for (fp = fonly; *fp; fp++) {
+	if (OPT_ISSET(ops,STOUC(*fp)) && !OPT_ISSET(ops,'F')) {
+	    zwarnnam(nam, "-%c is only allowed with -F", *fp);
+	    return 1;
+	}
+    }
     queue_signals();
-    if (OPT_ISSET(ops,'e'))
+    if (OPT_ISSET(ops, 'F'))
+	ret = bin_zmodload_features(nam, args, ops);
+    else if (OPT_ISSET(ops,'e'))
 	ret = bin_zmodload_exist(nam, args, ops);
     else if (OPT_ISSET(ops,'d'))
 	ret = bin_zmodload_dep(nam, args, ops);
@@ -1471,9 +2440,7 @@ unload_module(Module m, LinkNode node)
     }
     if ((m->flags & MOD_INIT_S) &&
 	!(m->flags & MOD_UNLOAD) &&
-	((m->flags & MOD_LINKED) ?
-	 (m->u.linked && m->u.linked->cleanup(m)) :
-	 (m->u.handle && cleanup_module(m))))
+	do_cleanup_module(m))
 	return 1;
     else {
 	int del = (m->flags & MOD_UNLOAD);
@@ -1622,608 +2589,274 @@ bin_zmodload_load(char *nam, char **args, Options ops)
 	return 0;
     } else {
 	/* load modules */
-	for (; *args; args++)
-	    if (!require_module(nam, *args, 1, (!OPT_ISSET(ops,'i'))))
-		ret = 1;
-
-	return ret;
-    }
-}
-
-/* The list of module-defined conditions. */
-
-/**/
-mod_export Conddef condtab;
-
-/* This gets a condition definition with the given name. The first        *
- * argument says if we have to look for an infix condition. The last      *
- * argument is non-zero if we should autoload modules if needed. */
-
-/**/
-Conddef
-getconddef(int inf, char *name, int autol)
-{
-    Conddef p;
-    int f = 1;
-
-    do {
-	for (p = condtab; p; p = p->next) {
-	    if ((!!inf == !!(p->flags & CONDF_INFIX)) &&
-		!strcmp(name, p->name))
-		break;
-	}
-	if (autol && p && p->module) {
-	    /* This is a definition for an autoloaded condition, load the *
-	     * module if we haven't tried that already. */
-	    if (f) {
-		load_module_silence(p->module, 0);
-		f = 0;
-		p = NULL;
-	    } else {
-		deleteconddef(p);
-		return NULL;
-	    }
-	} else
-	    break;
-    } while (!p);
-    return p;
-}
-
-/* This adds the given condition definition. The return value is zero on *
- * success and 1 on failure. If there is a matching definition for an    *
- * autoloaded condition, it is removed. */
-
-/**/
-int
-addconddef(Conddef c)
-{
-    Conddef p = getconddef((c->flags & CONDF_INFIX), c->name, 0);
-
-    if (p) {
-	if (!p->module || (p->flags & CONDF_ADDED))
-	    return 1;
-	/* There is an autoload definition. */
-
-	deleteconddef(p);
-    }
-    c->next = condtab;
-    condtab = c;
-    return 0;
-}
-
-/* This adds multiple condition definitions. This is like addbuiltins(). */
-
-/**/
-mod_export int
-addconddefs(char const *nam, Conddef c, int size)
-{
-    int hads = 0, hadf = 0;
-
-    while (size--) {
-	if (c->flags & CONDF_ADDED) {
-	    c++;
-	    continue;
-	}
-	if (addconddef(c)) {
-	    zwarnnam(nam, "name clash when adding condition `%s'", c->name);
-	    hadf = 1;
-	} else {
-	    c->flags |= CONDF_ADDED;
-	    hads = 2;
+	for (; *args; args++) {
+	    int tmpret = require_module(nam, *args, NULL);
+	    if (tmpret && ret != 1)
+		ret = tmpret;
 	}
-	c++;
-    }
-    return hadf ? hads : 1;
-}
-
-/* This list of hook functions defined. */
 
-/**/
-Hookdef hooktab;
-
-/* Find a hook definition given the name. */
-
-/**/
-Hookdef
-gethookdef(char *n)
-{
-    Hookdef p;
-
-    for (p = hooktab; p; p = p->next)
-	if (!strcmp(n, p->name))
-	    return p;
-    return NULL;
-}
-
-/* This adds the given hook definition. The return value is zero on      *
- * success and 1 on failure.                                             */
-
-/**/
-int
-addhookdef(Hookdef h)
-{
-    if (gethookdef(h->name))
-	return 1;
-
-    h->next = hooktab;
-    hooktab = h;
-    h->funcs = znewlinklist();
-
-    return 0;
-}
-
-/* This adds multiple hook definitions. This is like addbuiltins(). */
-
-/**/
-mod_export int
-addhookdefs(char const *nam, Hookdef h, int size)
-{
-    int hads = 0, hadf = 0;
-
-    while (size--) {
-	if (addhookdef(h)) {
-	    zwarnnam(nam, "name clash when adding hook `%s'", h->name);
-	    hadf = 1;
-	} else
-	    hads = 2;
-	h++;
+	return ret;
     }
-    return hadf ? hads : 1;
 }
 
-/* Delete hook definitions. */
-
 /**/
-int
-deletehookdef(Hookdef h)
+static int
+bin_zmodload_features(char *nam, char **args, Options ops)
 {
-    Hookdef p, q;
+    char *modname = *args;
 
-    for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next);
-
-    if (!p)
+    if (!modname) {
+	zwarnnam(nam, "-F requires a module name");
 	return 1;
-
-    if (q)
-	q->next = p->next;
-    else
-	hooktab = p->next;
-    freelinklist(p->funcs, NULL);
-    return 0;
-}
-
-/**/
-mod_export int
-deletehookdefs(UNUSED(char const *nam), Hookdef h, int size)
-{
-    while (size--) {
-	deletehookdef(h);
-	h++;
     }
-    return 1;
-}
-
-/* Add a function to a hook. */
-
-/**/
-int
-addhookdeffunc(Hookdef h, Hookfn f)
-{
-    zaddlinknode(h->funcs, (void *) f);
-
-    return 0;
-}
-
-/**/
-mod_export int
-addhookfunc(char *n, Hookfn f)
-{
-    Hookdef h = gethookdef(n);
-
-    if (h)
-	return addhookdeffunc(h, f);
-    return 1;
-}
-
-/* Delete a function from a hook. */
-
-/**/
-int
-deletehookdeffunc(Hookdef h, Hookfn f)
-{
-    LinkNode p;
-
-    for (p = firstnode(h->funcs); p; incnode(p))
-	if (f == (Hookfn) getdata(p)) {
-	    remnode(h->funcs, p);
-	    return 0;
-	}
-    return 1;
-}
-
-/**/
-mod_export int
-deletehookfunc(char *n, Hookfn f)
-{
-    Hookdef h = gethookdef(n);
-
-    if (h)
-	return deletehookdeffunc(h, f);
-    return 1;
-}
-
-/* Run the function(s) for a hook. */
-
-/**/
-mod_export int
-runhookdef(Hookdef h, void *d)
-{
-    if (empty(h->funcs)) {
-	if (h->def)
-	    return h->def(h, d);
-	return 0;
-    } else if (h->flags & HOOKF_ALL) {
-	LinkNode p;
-	int r;
-
-	for (p = firstnode(h->funcs); p; incnode(p))
-	    if ((r = ((Hookfn) getdata(p))(h, d)))
-		return r;
-	if (h->def)
-	    return h->def(h, d);
-	return 0;
-    } else
-	return ((Hookfn) getdata(lastnode(h->funcs)))(h, d);
-}
-
-/**/
-int
-runhook(char *n, void *d)
-{
-    Hookdef h = gethookdef(n);
-
-    if (h)
-	return runhookdef(h, d);
-    return 0;
-}
-
-/* This adds the given parameter definition. The return value is zero on *
- * success and 1 on failure. */
-
-/**/
-int
-addparamdef(Paramdef d)
-{
-    Param pm;
-
-    if ((pm = (Param) gethashnode2(paramtab, d->name)))
-	unsetparam_pm(pm, 0, 1);
-
-    if (!(pm = createparam(d->name, d->flags)) &&
-	!(pm = (Param) paramtab->getnode(paramtab, d->name)))
-	return 1;
+    args++;
 
-    pm->level = 0;
-    pm->u.data = d->var;
-    if (d->gsu)
-	pm->gsu.i = (GsuInteger) d->gsu;
-    else {
+    if (OPT_ISSET(ops,'l') || OPT_ISSET(ops,'L') || OPT_ISSET(ops,'e')) {
 	/*
-	 * If no get/set/unset class, use the appropriate
-	 * variable type.
+	 * With option 'l', list all features one per line with + or -.
+	 * With option 'L', list as zmodload statement showing
+	 * only options turned on.
+	 * With both options, list as zmodload showing options
+	 * to be turned both on and off.
 	 */
-	switch (PM_TYPE(pm->node.flags)) {
-	case PM_SCALAR:
-	    pm->gsu.s = &varscalar_gsu;
-	    break;
-
-	case PM_INTEGER:
-	    pm->gsu.i = &varinteger_gsu;
-	    break;
-
-	case PM_ARRAY:
-	    pm->gsu.a = &vararray_gsu;
-	    break;
-
-	default:
-	    unsetparam_pm(pm, 0, 1);
+	LinkNode node;
+	Module m = NULL;
+	char **features, **fp, **arrset = NULL, **arrp = NULL;
+	int *enables = NULL, *ep;
+	char *param = OPT_ARG_SAFE(ops,'P');
+
+	node = find_module(modname, 1, NULL);
+	if (node)
+	    m = ((Module) getdata(node));
+	if (!m || !m->u.handle || (m->flags & MOD_UNLOAD)) {
+	    if (!OPT_ISSET(ops,'e'))
+		zwarnnam(nam, "module `%s' is not yet loaded", modname);
 	    return 1;
 	}
-    }
-
-    return 0;
-}
-
-/* This adds multiple parameter definitions. This is like addbuiltins(). */
-
-/**/
-mod_export int
-addparamdefs(char const *nam, Paramdef d, int size)
-{
-    int hads = 0, hadf = 0;
-
-    while (size--) {
-	if (addparamdef(d)) {
-	    zwarnnam(nam, "error when adding parameter `%s'", d->name);
-	    hadf = 1;
-	} else
-	    hads = 2;
-	d++;
-    }
-    return hadf ? hads : 1;
-}
-
-/* Delete parameters defined. No error checking yet. */
-
-/**/
-int
-deleteparamdef(Paramdef d)
-{
-    unsetparam(d->name);
-    return 0;
-}
-
-/**/
-mod_export int
-deleteparamdefs(UNUSED(char const *nam), Paramdef d, int size)
-{
-    while (size--) {
-	deleteparamdef(d);
-	d++;
-    }
-    return 1;
-}
-
-/* This adds a definition for autoloading a module for a condition. */
-
-/**/
-int
-add_autocond(char *nam, int inf, char *module)
-{
-    Conddef c = (Conddef) zalloc(sizeof(*c));
-
-    c->name = ztrdup(nam);
-    c->flags = (inf  ? CONDF_INFIX : 0);
-    c->module = ztrdup(module);
-
-    if (addconddef(c)) {
-	zsfree(c->name);
-	zsfree(c->module);
-	zfree(c, sizeof(*c));
-
-	return 1;
-    }
-    return 0;
-}
-
-/* This removes the given condition definition from the list(s). If this *
- * is a definition for a autoloaded condition, the memory is freed. */
-
-/**/
-int
-deleteconddef(Conddef c)
-{
-    Conddef p, q;
-
-    for (p = condtab, q = NULL; p && p != c; q = p, p = p->next);
-
-    if (p) {
-	if (q)
-	    q->next = p->next;
-	else 
-	    condtab = p->next;
-		
-	if (p->module) {
-	    /* autoloaded, free it */
-	    zsfree(p->name);
-	    zsfree(p->module);
-	    zfree(p, sizeof(*p));
+	if (features_module(m, &features)) {
+	    if (!OPT_ISSET(ops,'e'))
+		zwarnnam(nam, "module `%s' does not support features", m->nam);
+	    return 1;
 	}
-	return 0;
-    }
-    return -1;
-}
-
-/* This removes multiple condition definitions (like deletebuiltins()). */
-
-/**/
-mod_export int
-deleteconddefs(char const *nam, Conddef c, int size)
-{
-    int hads = 0, hadf = 0;
-
-    while (size--) {
-	if (!(c->flags & CONDF_ADDED)) {
-	    c++;
-	    continue;
+	if (enables_module(m, &enables)) {
+	    /* this shouldn't ever happen, so don't silence this error */
+	    zwarnnam(nam, "error getting enabled features for module `%s'",
+		     m->nam);
+	    return 1;
 	}
-	if (deleteconddef(c)) {
-	    zwarnnam(nam, "condition `%s' already deleted", c->name);
-	    hadf = 1;
-	} else
-	    hads = 2;
-	c->flags &= ~CONDF_ADDED;
-	c++;
+	for (arrp = args; *arrp; arrp++) {
+	    char *arg = *arrp;
+	    int on;
+	    if (*arg == '-') {
+		on = 0;
+		arg++;
+	    } else if (*arg == '+') {
+		on = 1;
+		arg++;
+	    } else
+		on = -1;
+	    for (fp = features, ep = enables; *fp; fp++, ep++) {
+		if (!strcmp(arg, *fp)) {
+		    /* for -e, check given state, if any */
+		    if (OPT_ISSET(ops,'e') && on != -1 &&
+			on != (*ep & 1))
+			return 1;
+		    break;
+		}
+	    }
+	    if (!*fp) {
+		if (!OPT_ISSET(ops,'e'))
+		    zwarnnam(nam, "module `%s' has no such feature: %s",
+			     *arrp);
+		return 1;
+	    }
+	}
+	if (OPT_ISSET(ops,'e'))		/* yep, everything we want exists */
+	    return 0;
+	if (OPT_ISSET(ops,'P')) {
+	    int arrlen = 0;
+	    for (fp = features, ep = enables; *fp; fp++, ep++) {
+		if (OPT_ISSET(ops, 'L') && !OPT_ISSET(ops, 'l') &&
+		    !*ep)
+		    continue;
+		if (*args) {
+		    char **argp;
+		    for (argp = args; *argp; argp++) {
+			char *arg = *argp;
+			/* ignore +/- for consistency */
+			if (*arg == '+' || *arg == '-')
+			    arg++;
+			if (!strcmp(*fp, arg))
+			    break;
+		    }
+		    if (!*argp)
+			continue;
+		}
+		arrlen++;
+	    }
+	    arrp = arrset = zalloc(sizeof(char *) * (arrlen+1));
+	} else if (OPT_ISSET(ops, 'L'))
+	    printf("zmodload -F %s ", m->nam);
+	for (fp = features, ep = enables; *fp; fp++, ep++) {
+	    char *onoff;
+	    int term;
+	    if (*args) {
+		char **argp;
+		for (argp = args; *argp; argp++) {
+		    char *arg = *argp;
+		    if (*arg == '+' || *arg == '-')
+			arg++;
+		    if (!strcmp(*fp, *argp))
+			break;
+		}
+		if (!*argp)
+		    continue;
+	    }
+	    if (OPT_ISSET(ops, 'L') && !OPT_ISSET(ops, 'l')) {
+		if (!*ep)
+		    continue;
+		onoff = "";
+	    } else if (*ep) {
+		onoff = "+";
+	    } else {
+		onoff = "-";
+	    }
+	    if (param) {
+		*arrp++ = bicat(onoff, *fp);
+	    } else {
+		if (OPT_ISSET(ops, 'L') && fp[1]) {
+		    term = ' ';
+		} else {
+		    term = '\n';
+		}
+		printf("%s%s%c", onoff, *fp, term);
+	    }
+	}
+	if (param) {
+	    *arrp = NULL;
+	    if (!setaparam(param, arrset))
+		return 1;
+	}
+	return 0;
+    } else if (OPT_ISSET(ops,'P')) {
+	zwarnnam(nam, "-P can only be used with -l or -L");
+	return 1;
     }
-    return hadf ? hads : 1;
-}
-
-/* This adds a definition for autoloading a module for a parameter. */
 
-/**/
-void
-add_autoparam(char *nam, char *module)
-{
-    Param pm;
-
-    queue_signals();
-    if ((pm = (Param) gethashnode2(paramtab, nam)))
-	unsetparam_pm(pm, 0, 1);
-
-    pm = setsparam(nam, ztrdup(module));
-
-    pm->node.flags |= PM_AUTOLOAD;
-    unqueue_signals();
+    return require_module(nam, modname, args);
 }
 
-/* List of math functions. */
 
-/**/
-MathFunc mathfuncs;
+/************************************************************************
+ * Generic feature support.
+ * These functions are designed to be called by modules.
+ ************************************************************************/
 
-/**/
-void
-removemathfunc(MathFunc previous, MathFunc current)
-{
-    if (previous)
-	previous->next = current->next;
-    else
-	mathfuncs = current->next;
-
-    zsfree(current->name);
-    zsfree(current->module);
-    zfree(current, sizeof(*current));
-}
+/*
+ * Construct a features array out of the list of concrete
+ * features given, leaving space for any abstract features
+ * to be added by the module itself.
+ *
+ * Note the memory is from the heap.
+ */
 
 /**/
-MathFunc
-getmathfunc(char *name, int autol)
+mod_export char **
+featuresarray(char const *nam, Features f)
 {
-    MathFunc p, q = NULL;
-
-    for (p = mathfuncs; p; q = p, p = p->next)
-	if (!strcmp(name, p->name)) {
-	    if (autol && p->module && !(p->flags & MFF_USERFUNC)) {
-		char *n = dupstring(p->module);
-
-		removemathfunc(q, p);
-
-		load_module_silence(n, 0);
+    int bn_size = f->bn_size, cd_size = f->cd_size;
+    int pd_size = f->pd_size, mf_size = f->mf_size;
+    int features_size = bn_size + cd_size + pd_size + mf_size + f->n_abstract;
+    Builtin bnp = f->bn_list;
+    Conddef cdp = f->cd_list;
+    Paramdef pdp = f->pd_list;
+    MathFunc mfp = f->mf_list;
+    char **features = (char **)zhalloc((features_size + 1) * sizeof(char *));
+    char **featurep = features;
 
-		return getmathfunc(name, 0);
-	    }
-	    return p;
-	}
+    while (bn_size--)
+	*featurep++ = dyncat("b:", (bnp++)->node.nam);
+    while (cd_size--)
+	*featurep++ = dyncat("c:", (cdp++)->name);
+    while (pd_size--)
+	*featurep++ = dyncat("p:", (pdp++)->name);
+    while (mf_size--)
+	*featurep++ = dyncat("f:", (mfp++)->name);
 
-    return NULL;
+    features[features_size] = NULL;
+    return features;
 }
 
+/*
+ * Return the current set of enables for the features in a
+ * module using heap memory.  Leave space for abstract
+ * features.  The array is not zero terminated.
+ */
 /**/
-mod_export int
-addmathfunc(MathFunc f)
+mod_export int *
+getfeatureenables(char const *nam, Features f)
 {
-    MathFunc p, q = NULL;
-
-    if (f->flags & MFF_ADDED)
-	return 1;
-
-    for (p = mathfuncs; p; q = p, p = p->next)
-	if (!strcmp(f->name, p->name)) {
-	    if (p->module && !(p->flags & MFF_USERFUNC)) {
-		/*
-		 * Autoloadable, replace.
-		 */
-		removemathfunc(q, p);
-		break;
-	    }
-	    return 1;
-	}
+    int bn_size = f->bn_size, cd_size = f->cd_size;
+    int pd_size = f->pd_size, mf_size = f->mf_size;
+    int features_size = bn_size + cd_size + pd_size + mf_size + f->n_abstract;
+    Builtin bnp = f->bn_list;
+    Conddef cdp = f->cd_list;
+    Paramdef pdp = f->pd_list;
+    MathFunc mfp = f->mf_list;
+    int *enables = zhalloc(sizeof(int) * features_size);
+    int *enablep = enables;
 
-    f->flags |= MFF_ADDED;
-    f->next = mathfuncs;
-    mathfuncs = f;
+    while (bn_size--)
+	*enablep++ = ((bnp++)->node.flags & BINF_ADDED) ? 1 : 0;
+    while (cd_size--)
+	*enablep++ = ((cdp++)->flags & CONDF_ADDED) ? 1 : 0;
+    while (pd_size--)
+	*enablep++ = (pdp++)->pm ? 1 : 0;
+    while (mf_size--)
+	*enablep++ = ((mfp++)->flags & MFF_ADDED) ? 1 : 0;
 
-    return 0;
+    return enables;
 }
 
-/**/
-mod_export int
-addmathfuncs(char const *nam, MathFunc f, int size)
-{
-    int hads = 0, hadf = 0;
-
-    while (size--) {
-	if (f->flags & MFF_ADDED) {
-	    f++;
-	    continue;
-	}
-	if (addmathfunc(f)) {
-	    zwarnnam(nam, "name clash when adding math function `%s'",
-		     f->name);
-	    hadf = 1;
-	} else
-	    hads = 2;
-	f++;
-    }
-    return hadf ? hads : 1;
-}
-
-/**/
-int
-add_automathfunc(char *nam, char *module)
-{
-    MathFunc f = (MathFunc) zalloc(sizeof(*f));
-
-    f->name = ztrdup(nam);
-    f->module = ztrdup(module);
-    f->flags = 0;
-
-    if (addmathfunc(f)) {
-	zsfree(f->name);
-	zsfree(f->module);
-	zfree(f, sizeof(*f));
-
-	return 1;
-    }
-    f->flags &= ~MFF_ADDED; /* still to autoload, not added yet */
-
-    return 0;
-}
+/*
+ * Add or remove the concrete features passed in arguments,
+ * depending on the corresponding element of the array e.
+ * If e is NULL, disable everything.
+ * Return 0 for success, 1 for failure; does not attempt
+ * to imitate the return values of addbuiltins() etc.
+ * Any failure in adding a requested feature is an
+ * error.
+ */
 
 /**/
 mod_export int
-deletemathfunc(MathFunc f)
+setfeatureenables(char const *nam, Features f, int *e)
 {
-    MathFunc p, q;
-
-    for (p = mathfuncs, q = NULL; p && p != f; q = p, p = p->next);
-
-    if (p) {
-	if (q)
-	    q->next = f->next;
-	else
-	    mathfuncs = f->next;
-
-	/* the following applies to both unloaded and user-defined functions */
-	if (f->module) {
-	    zsfree(f->name);
-	    zsfree(f->module);
-	    zfree(f, sizeof(*f));
-	} else
-	    f->flags &= ~MFF_ADDED;
+    int ret = 0;
 
-	return 0;
-    }
-    return -1;
+    if (f->bn_size && setbuiltins(nam, f->bn_list, f->bn_size, e) != 1)
+	ret = 1;
+    if (e)
+	e += f->bn_size;
+    if (f->cd_size && setconddefs(nam, f->cd_list, f->cd_size, e) != 1)
+	ret = 1;
+    if (e)
+	e += f->cd_size;
+    if (f->pd_size && setparamdefs(nam, f->pd_list, f->pd_size, e) != 1)
+	ret = 1;
+    if (e)
+	e += f->pd_size;
+    if (f->mf_size && setmathfuncs(nam, f->mf_list, f->mf_size, e) != 1)
+	ret = 1;
+    return ret;
 }
-
+	    
 /**/
 mod_export int
-deletemathfuncs(char const *nam, MathFunc f, int size)
+handlefeatures(char *nam, Features f, int **enables)
 {
-    int hads = 0, hadf = 0;
-
-    while (size--) {
-	if (!(f->flags & MFF_ADDED)) {
-	    f++;
-	    continue;
-	}
-	if (deletemathfunc(f)) {
-	    zwarnnam(nam, "math function `%s' already deleted", f->name);
-	    hadf = 1;
-	} else
-	    hads = 2;
-	f++;
-    }
-    return hadf ? hads : 1;
+    if (!enables || *enables)
+	return setfeatureenables(nam, f, *enables);
+    *enables = getfeatureenables(nam, f);
+    return 0;
 }
diff --git a/Src/params.c b/Src/params.c
index 14ff4f6ca..31a65811f 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -419,7 +419,7 @@ getparamnode(HashTable ht, char *nam)
     if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) {
 	char *mn = dupstring(pm->u.str);
 
-	if (!load_module(mn))
+	if (load_module(mn, NULL) == 1)
 	    return NULL;
 	hn = gethashnode2(ht, nam);
 	if (((Param) hn) == pm && (pm->node.flags & PM_AUTOLOAD)) {
@@ -840,6 +840,47 @@ createparam(char *name, int flags)
     return pm;
 }
 
+/* Empty dummy function for special hash parameters. */
+
+/**/
+static void
+shempty(void)
+{
+}
+
+/* Create a simple special hash parameter. */
+
+/**/
+mod_export Param
+createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan, int flags)
+{
+    Param pm;
+    HashTable ht;
+
+    if (!(pm = createparam(name, PM_SPECIAL|PM_HASHED|flags)))
+	return NULL;
+
+    pm->level = pm->old ? locallevel : 0;
+    pm->gsu.h = (flags & PM_READONLY) ? &stdhash_gsu :
+	&nullsethash_gsu;
+    pm->u.hash = ht = newhashtable(0, name, NULL);
+
+    ht->hash        = hasher;
+    ht->emptytable  = (TableFunc) shempty;
+    ht->filltable   = NULL;
+    ht->addnode     = (AddNodeFunc) shempty;
+    ht->getnode     = ht->getnode2 = get;
+    ht->removenode  = (RemoveNodeFunc) shempty;
+    ht->disablenode = NULL;
+    ht->enablenode  = NULL;
+    ht->freenode    = (FreeNodeFunc) shempty;
+    ht->printnode   = printparamnode;
+    ht->scantab     = scan;
+
+    return pm;
+}
+
+
 /* Copy a parameter */
 
 /**/
@@ -4117,7 +4158,7 @@ scanendscope(HashNode hn, UNUSED(int flags))
 	    if (pm->env)
 		delenv(pm);
 
-	    if (!(tpm->node.flags & PM_NORESTORE))
+	    if (!(tpm->node.flags & (PM_NORESTORE|PM_READONLY)))
 		switch (PM_TYPE(pm->node.flags)) {
 		case PM_SCALAR:
 		    pm->gsu.s->setfn(pm, tpm->u.str);
diff --git a/Src/zsh.h b/Src/zsh.h
index a80a6fd99..f7255c6e7 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -77,7 +77,7 @@ typedef mnumber (*StrMathFunc)(char *, char *, int);
 struct mathfunc {
     MathFunc next;
     char *name;
-    int flags;
+    int flags;			/* MFF_* flags defined below */
     NumMathFunc nfunc;
     StrMathFunc sfunc;
     char *module;
@@ -93,6 +93,7 @@ struct mathfunc {
 /* Math function is implemented by a shell function */
 #define MFF_USERFUNC 4
 
+
 #define NUMMATHFUNC(name, func, min, max, id) \
     { NULL, name, 0, func, NULL, NULL, min, max, id }
 #define STRMATHFUNC(name, func, id) \
@@ -375,6 +376,7 @@ typedef struct builtin   *Builtin;
 typedef struct cmdnam    *Cmdnam;
 typedef struct complist  *Complist;
 typedef struct conddef   *Conddef;
+typedef struct features  *Features;
 typedef struct funcstack *Funcstack;
 typedef struct funcwrap  *FuncWrap;
 typedef struct hashnode  *HashNode;
@@ -1166,14 +1168,40 @@ struct module {
 #define MOD_INIT_B  (1<<5)
 #define MOD_ALIAS   (1<<6)
 
-typedef int (*Module_func) _((Module));
+typedef int (*Module_generic_func) _((void));
+typedef int (*Module_void_func) _((Module));
+typedef int (*Module_features_func) _((Module, char ***));
+typedef int (*Module_enables_func) _((Module, int **));
 
 struct linkedmod {
     char *name;
-    Module_func setup;
-    Module_func boot;
-    Module_func cleanup;
-    Module_func finish;
+    Module_void_func setup;
+    Module_features_func features;
+    Module_enables_func enables;
+    Module_void_func boot;
+    Module_void_func cleanup;
+    Module_void_func finish;
+};
+
+/*
+ * Structure combining all the concrete features available in
+ * a module and with space for information about abstract features.
+ */
+struct features {
+    /* List of builtins provided by the module and the size thereof */
+    Builtin bn_list;
+    int bn_size;
+    /* List of conditions provided by the module and the size thereof */
+    Conddef cd_list;
+    int cd_size;
+    /* List of parameters provided by the module and the size thereof */
+    Paramdef pd_list;
+    int pd_size;
+    /* List of math functions provided by the module and the size thereof */
+    MathFunc mf_list;
+    int mf_size;
+    /* Number of abstract features */
+    int n_abstract;
 };
 
 /* C-function hooks */
@@ -1422,26 +1450,65 @@ struct tieddata {
 #define PF_ASSIGN	0x02	/* argument handled like the RHS of foo=bar */
 #define PF_SINGLE	0x04	/* single word substitution */
 
+/*
+ * Structure for adding parameters in a module.
+ * The flags should declare the type; note PM_SCALAR is zero.
+ *
+ * Special hashes are recognized by getnfn so the PM_HASHED
+ * is optional.  These get slightly non-standard attention:
+ * the function createspecialhash is used to create them.
+ *
+ * The get/set/unset attribute may be NULL; in that case the
+ * parameter is assigned methods suitable for handling the
+ * tie variable var, if that is not NULL, else standard methods.
+ *
+ * pm is set when the parameter is added to the parameter table
+ * and serves as a flag that the parameter has been added.
+ */
 struct paramdef {
     char *name;
     int flags;
-    void *var;
-    void *gsu;			/* get/set/unset structure */
+    void *var;			/* tied internal variable, if any */
+    const void *gsu;		/* get/set/unset structure, if special */
+    GetNodeFunc getnfn;		/* function to get node, if special hash */
+    ScanTabFunc scantfn;	/* function to scan table, if special hash */
+    Param pm;			/* structure inserted into param table */
 };
 
+/*
+ * Shorthand for common uses of adding parameters, with no special
+ * hash properties.
+ */
 #define PARAMDEF(name, flags, var, gsu) \
-    { name, flags, (void *) var, (void *) gsu, }
+    { name, flags, (void *) var, (void *) gsu, \
+	    NULL, NULL, NULL \
+    }
 /*
  * Note that the following definitions are appropriate for defining
  * parameters that reference a variable (var).  Hence the get/set/unset
  * methods used will assume var needs dereferencing to get the value.
  */
 #define INTPARAMDEF(name, var) \
-    { name, PM_INTEGER, (void *) var, NULL }
+    { name, PM_INTEGER, (void *) var, NULL,  NULL, NULL, NULL }
 #define STRPARAMDEF(name, var) \
-    { name, PM_SCALAR, (void *) var, NULL }
+    { name, PM_SCALAR, (void *) var, NULL, NULL, NULL, NULL }
 #define ARRPARAMDEF(name, var) \
-    { name, PM_ARRAY, (void *) var, NULL }
+    { name, PM_ARRAY, (void *) var, NULL, NULL, NULL, NULL }
+/*
+ * The following is appropriate for a module function that behaves
+ * in a special fashion.  Parameters used in a module that don't
+ * have special behaviour shouldn't be declared in a table but
+ * should just be added with the standard parameter functions.
+ *
+ * These parameters are not marked as removable, since they
+ * shouldn't be loaded as local parameters, unlike the special
+ * Zle parameters that are added and removed on each call to Zle.
+ * We add the PM_REMOVABLE flag when removing the feature corresponding
+ * to the parameter.
+ */
+#define SPECIALPMDEF(name, flags, gsufn, getfn, scanfn) \
+    { name, flags | PM_SPECIAL | PM_HIDE | PM_HIDEVAL, \
+	    NULL, gsufn, getfn, scanfn, NULL }
 
 #define setsparam(S,V) assignsparam(S,V,0)
 #define setaparam(S,V) assignaparam(S,V,0)