about summary refs log tree commit diff
path: root/Src/Modules
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/Modules
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/Modules')
-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
23 files changed, 713 insertions, 618 deletions
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);
 }
 
 /**/