summary refs log tree commit diff
path: root/Src/module.c
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2007-06-26 19:38:34 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2007-06-26 19:38:34 +0000
commit4be5febd94a453c4ed67eb00e43d2107453e6563 (patch)
tree419d1dd068f18cd1c836a3c8c30441e1d199cd85 /Src/module.c
parentcc2867746aa09f386e5cad8ec8082cfe54c1e4a0 (diff)
downloadzsh-4be5febd94a453c4ed67eb00e43d2107453e6563.tar.gz
zsh-4be5febd94a453c4ed67eb00e43d2107453e6563.tar.xz
zsh-4be5febd94a453c4ed67eb00e43d2107453e6563.zip
23606: make all module feature autoloads pass through one function
Diffstat (limited to 'Src/module.c')
-rw-r--r--Src/module.c551
1 files changed, 259 insertions, 292 deletions
diff --git a/Src/module.c b/Src/module.c
index ef158c6a3..f5f500315 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -45,6 +45,35 @@ char **module_path;
 /**/
 mod_export LinkList modules;
 
+/*
+ * Bit flags passed as the "flags" argument of a autofeaturefn_t.
+ */
+enum {
+    /* `-i' option: ignore errors pertaining to redefinitions */
+    AUTOFEAT_IGNORE = 0x0001,
+    /* If a condition, condition is infix rather than prefix */
+    AUTOFEAT_INFIX = 0x0002
+};
+
+/*
+ * All functions to add or remove autoloadable features fit
+ * the following prototype.
+ *
+ * "module" is the name of the module.
+ *
+ * "feature" is the name of the feature, minus any type prefix.
+ *
+ * "flags" is a set of the bits above.
+ *
+ * The return value is 0 for success, a negative value for failure with no
+ * message needed, and one of the following to indicate the calling
+ * function should print a message:
+ *
+ * 1:  failed to add [type] `[feature]'
+ * 2:  [feature]: no such [type]
+ * 3:  [feature]: [type] is already defined
+ */
+typedef int (*autofeaturefn_t)(char *module, char *feature, int flags);
 
 /************************************************************************
  * zsh/main standard module functions
@@ -196,25 +225,18 @@ addbuiltin(Builtin b)
  * with the specified name already exists.                           */
 
 /**/
-int
-add_autobin(char *cmdnam, char *bnam, char *module, int opt_i)
+static int
+add_autobin(char *module, char *bnam, int flags)
 {
     Builtin bn;
 
-    if (strchr(bnam, '/')) {
-	zwarnnam(cmdnam, "%s: `/' is illegal in a builtin", bnam);
-	return 1;
-    }
-
     bn = zshcalloc(sizeof(*bn));
     bn->node.nam = ztrdup(bnam);
     bn->optstr = ztrdup(module);
     if (addbuiltin(bn)) {
 	builtintab->freenode(&bn->node);
-	if (!opt_i) {
-	    zwarnnam(cmdnam, "failed to add builtin %s", bnam);
+	if (!(flags & AUTOFEAT_IGNORE))
 	    return 1;
-	}
     }
     return 0;
 }
@@ -239,17 +261,14 @@ deletebuiltin(char *nam)
 
 /**/
 static int
-del_autobin(char *cmdnam, char *bnam, int opt_i)
+del_autobin(UNUSED(char *module), char *bnam, int flags)
 {
     Builtin bn = (Builtin) builtintab->getnode2(builtintab, bnam);
     if (!bn) {
-	if(!opt_i) {
-	    zwarnnam(cmdnam, "%s: no such builtin", bnam);
-	    return 1;
-	}
+	if(!(flags & AUTOFEAT_IGNORE))
+	    return 2;
     } else if (bn->node.flags & BINF_ADDED) {
-	zwarnnam(cmdnam, "%s: builtin is already defined", bnam);
-	return 1;
+	return 3;
     } else
 	deletebuiltin(bnam);
 
@@ -557,20 +576,15 @@ setconddefs(char const *nam, Conddef c, int size, int *e)
 /* This adds a definition for autoloading a module for a condition. */
 
 /**/
-int
-add_autocond(char *cmdnam, char *cnam, int inf, char *module, int opt_i)
+static int
+add_autocond(char *module, char *cnam, int flags)
 {
     Conddef c;
 
-    if (strchr(cnam, '/')) {
-	zwarnnam(cmdnam, "%s: `/' is illegal in a condition", cnam);
-	return 1;
-    }
-
     c = (Conddef) zalloc(sizeof(*c));
 
     c->name = ztrdup(cnam);
-    c->flags = (inf  ? CONDF_INFIX : 0);
+    c->flags = ((flags & AUTOFEAT_INFIX) ? CONDF_INFIX : 0);
     c->module = ztrdup(module);
 
     if (addconddef(c)) {
@@ -578,10 +592,8 @@ add_autocond(char *cmdnam, char *cnam, int inf, char *module, int opt_i)
 	zsfree(c->module);
 	zfree(c, sizeof(*c));
 
-	if (!opt_i) {
-	    zwarnnam(cmdnam, "failed to add condition `%s'", cnam);
+	if (!(flags & AUTOFEAT_IGNORE))
 	    return 1;
-	}
     }
     return 0;
 }
@@ -590,18 +602,16 @@ add_autocond(char *cmdnam, char *cnam, int inf, char *module, int opt_i)
 
 /**/
 static int
-del_autocond(char *cmdnam, char *cnam, int infix, int opt_i)
+del_autocond(UNUSED(char *modnam), char *cnam, int flags)
 {
-    Conddef cd = getconddef(infix, cnam, 0);
+    Conddef cd = getconddef((flags & AUTOFEAT_INFIX) ? 1 : 0, cnam, 0);
 
     if (!cd) {
-	if (!opt_i) {
-	    zwarnnam(cmdnam, "%s: no such condition", cnam);
-	    return 1;
+	if (!(flags & AUTOFEAT_IGNORE)) {
+	    return 2;
 	}
     } else if (cd->flags & CONDF_ADDED) {
-	zwarnnam(cmdnam, "%s: condition is already defined", cnam);
-	return 1;
+	return 3;
     } else
 	deleteconddef(cd);
 
@@ -959,29 +969,23 @@ setparamdefs(char const *nam, Paramdef d, int size, int *e)
 /* This adds a definition for autoloading a module for a parameter. */
 
 /**/
-int
-add_autoparam(char *cmdnam, char *pnam, char *module, int opt_i)
+static int
+add_autoparam(char *module, char *pnam, int flags)
 {
     Param pm;
     int ret;
 
-    if (strchr(pnam, '/')) {
-	zwarnnam(cmdnam, "%s: `/' is illegal in a parameter", pnam);
-	return 1;
-    }
-
     queue_signals();
-    if ((ret = checkaddparam(pnam, opt_i))) {
+    if ((ret = checkaddparam(pnam, (flags & AUTOFEAT_IGNORE)))) {
 	unqueue_signals();
 	/*
-	 * checkaddparam() has already printed a message
-	 * if one was needed.  If it wasn't because of -i,
-	 * ret is 2; for consistency with other add_auto*
-	 * functions we return status 0 to indicate there's
-	 * already such a parameter and we've been told not
-	 * to worry if so.
+	 * checkaddparam() has already printed a message if one was
+	 * needed.  If it wasn't owing to the presence of -i, ret is 2;
+	 * for consistency with other add_auto* functions we return
+	 * status 0 to indicate there's already such a parameter and
+	 * we've been told not to worry if so.
 	 */
-	return ret == 2 ? 0 : 1;
+	return ret == 2 ? 0 : -1;
     }
 
     pm = setsparam(pnam, ztrdup(module));
@@ -996,18 +1000,15 @@ add_autoparam(char *cmdnam, char *pnam, char *module, int opt_i)
 
 /**/
 static int
-del_autoparam(char *cmdnam, char *pnam, int opt_i)
+del_autoparam(UNUSED(char *modnam), char *pnam, int flags)
 {
     Param pm = (Param) gethashnode2(paramtab, pnam);
 
     if (!pm) {
-	if (!opt_i) {
-	    zwarnnam(cmdnam, "%s: no such parameter", pnam);
-	    return 1;
-	}
+	if (!(flags & AUTOFEAT_IGNORE))
+	    return 2;
     } else if (!(pm->node.flags & PM_AUTOLOAD)) {
-	zwarnnam(cmdnam, "%s: parameter is already defined", pnam);
-	return 1;
+	return 3;
     } else
 	unsetparam_pm(pm, 0, 1);
 
@@ -1169,16 +1170,11 @@ setmathfuncs(char const *nam, MathFunc f, int size, int *e)
 /* Add an autoload definition for a math function. */
 
 /**/
-int
-add_automathfunc(char *cmdnam, char *fnam, char *module, int opt_i)
+static int
+add_automathfunc(char *module, char *fnam, int flags)
 {
     MathFunc f;
 
-    if (strchr(fnam, '/')) {
-	zwarnnam(cmdnam, "%s: `/' is illegal in a math function", fnam);
-	return 1;
-    }
-
     f = (MathFunc) zalloc(sizeof(*f));
 
     f->name = ztrdup(fnam);
@@ -1190,10 +1186,8 @@ add_automathfunc(char *cmdnam, char *fnam, char *module, int opt_i)
 	zsfree(f->module);
 	zfree(f, sizeof(*f));
 
-	if (!opt_i) {
-	    zwarnnam(cmdnam, "failed to add math function `%s'", fnam);
+	if (!(flags & AUTOFEAT_IGNORE))
 	    return 1;
-	}
     }
 
     return 0;
@@ -1203,18 +1197,15 @@ add_automathfunc(char *cmdnam, char *fnam, char *module, int opt_i)
 
 /**/
 static int
-del_automathfunc(char *cmdnam, char *fnam, int opt_i)
+del_automathfunc(UNUSED(char *modnam), char *fnam, int flags)
 {
     MathFunc f = getmathfunc(fnam, 0);
     
     if (!f) {
-	if (!opt_i) {
-	    zwarnnam(cmdnam, "%s: no such math function", fnam);
-	    return 1;
-	}
+	if (!(flags & AUTOFEAT_IGNORE))
+	    return 2;
     } else if (f->flags & MFF_ADDED) {
-	zwarnnam(cmdnam, "%s: math function is already defined", fnam);
-	return 1;
+	return 3;
     } else
 	deletemathfunc(f);
 
@@ -1400,9 +1391,22 @@ do_load_module(char const *name, int silent)
 /**/
 #endif /* !DYNAMIC */
 
+/* Bits in the second argument to find_module. */
+enum {
+    /*
+     * Resolve any aliases to the underlying module.
+     */
+    FINDMOD_ALIASP = 0x0001,
+    /*
+     * Create an element for the module in the list if
+     * it is not found.
+     */
+    FINDMOD_CREATE = 0x0002,
+};
+
 /*
  * Find a module in the list.
- * If aliasp is non-zero, resolve any aliases to the underlying module.
+ * flags is a set of bits defined in the enum above.
  * If namep is set, this is set to point to the last alias value resolved,
  *   even if that module was not loaded. or the module name if no aliases.
  *   Hence this is always the physical module to load in a chain of aliases.
@@ -1415,7 +1419,7 @@ do_load_module(char const *name, int silent)
  */
 /**/
 static LinkNode
-find_module(const char *name, int aliasp, const char **namep)
+find_module(const char *name, int flags, const char **namep)
 {
     Module m;
     LinkNode node;
@@ -1423,17 +1427,21 @@ find_module(const char *name, int aliasp, const char **namep)
     for (node = firstnode(modules); node; incnode(node)) {
 	m = (Module) getdata(node);
 	if (!strcmp(m->nam, name)) {
-	    if (aliasp && (m->flags & MOD_ALIAS)) {
+	    if ((flags & FINDMOD_ALIASP) && (m->flags & MOD_ALIAS)) {
 		if (namep)
 		    *namep = m->u.alias;
-		return find_module(m->u.alias, 1, namep);
+		return find_module(m->u.alias, flags, namep);
 	    }
 	    if (namep)
 		*namep = m->nam;
 	    return node;
 	}
     }
-    return NULL;
+    if (!(flags & FINDMOD_CREATE))
+	return NULL;
+    m = zshcalloc(sizeof(*m));
+    m->nam = ztrdup(name);
+    return zaddlinknode(modules, m);
 }
 
 /*
@@ -1468,7 +1476,7 @@ module_loaded(const char *name)
     LinkNode node;
     Module m;
 
-    return ((node = find_module(name, 1, NULL)) &&
+    return ((node = find_module(name, FINDMOD_ALIASP, NULL)) &&
 	    (m = ((Module) getdata(node)))->u.handle &&
 	    !(m->flags & MOD_UNLOAD));
 }
@@ -1916,7 +1924,7 @@ load_module(char const *name, char **enablesstr, int silent)
      * is the right one.
      */
     queue_signals();
-    if (!(node = find_module(name, 1, &name))) {
+    if (!(node = find_module(name, FINDMOD_ALIASP, &name))) {
 	if (!(linked = module_linked(name)) &&
 	    !(handle = do_load_module(name, silent))) {
 	    unqueue_signals();
@@ -2082,12 +2090,7 @@ add_dep(const char *name, char *from)
      * Better make sure.  (There's no problem making a an alias which
      * *points* to a module with dependencies, of course.)
      */
-    if (!(node = find_module(name, 1, &name))) {
-	m = zshcalloc(sizeof(*m));
-	m->nam = ztrdup(name);
-	zaddlinknode(modules, m);
-    } else
-	m = (Module) getdata(node);
+    m = getdata(find_module(name, FINDMOD_ALIASP|FINDMOD_CREATE, &name));
     if (!m->deps)
 	m->deps = znewlinklist();
     for (node = firstnode(m->deps);
@@ -2146,7 +2149,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
     int ops_bcpf = OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c') || 
 	OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f');
     int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u');
-    int ret = 1;
+    int ret = 1, autoopts;
     /* options only allowed with -F */
     char *fonly = "lP", *fp;
 
@@ -2195,20 +2198,17 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
 	ret = bin_zmodload_exist(nam, args, ops);
     else if (OPT_ISSET(ops,'d'))
 	ret = bin_zmodload_dep(nam, args, ops);
-    else if ((OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b')) && 
-	     !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f')))
-	ret = bin_zmodload_auto(nam, args, ops);
-    else if (OPT_ISSET(ops,'c') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p')))
-	ret = bin_zmodload_cond(nam, args, ops);
-    else if (OPT_ISSET(ops,'f') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p')))
-	ret = bin_zmodload_math(nam, args, ops);
-    else if (OPT_ISSET(ops,'p') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c')))
-	ret = bin_zmodload_param(nam, args, ops);
-    else if (!(OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b') || 
-	       OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p')))
+    else if ((autoopts = OPT_ISSET(ops, 'b') + OPT_ISSET(ops, 'c') +
+	      OPT_ISSET(ops, 'p') + OPT_ISSET(ops, 'f')) ||
+	     /* zmodload -a is equivalent to zmodload -ab, annoyingly */
+	     OPT_ISSET(ops, 'a')) {
+	if (autoopts > 1) {
+	    zwarnnam(nam, "use only one of -b, -c, or -p");
+	    ret = 1;
+	} else
+	    ret = bin_zmodload_auto(nam, args, ops);
+    } else
 	ret = bin_zmodload_load(nam, args, ops);
-    else
-	zwarnnam(nam, "use only one of -b, -c, or -p");
     unqueue_signals();
 
     return ret;
@@ -2344,7 +2344,7 @@ bin_zmodload_exist(UNUSED(char *nam), char **args, Options ops)
 	    if (m->flags & MOD_ALIAS) {
 		LinkNode node2;
 		if (OPT_ISSET(ops,'A') && 
-		    (node2 = find_module(m->u.alias, 1, NULL)))
+		    (node2 = find_module(m->u.alias, FINDMOD_ALIASP, NULL)))
 		    m = (Module) getdata(node2);
 		else
 		    continue;
@@ -2359,7 +2359,7 @@ bin_zmodload_exist(UNUSED(char *nam), char **args, Options ops)
 	int ret = 0;
 
 	for (; !ret && *args; args++) {
-	    if (!(node = find_module(*args, 1, NULL))
+	    if (!(node = find_module(*args, FINDMOD_ALIASP, NULL))
 		|| !(m = (Module) getdata(node))->u.handle
 		|| (m->flags & MOD_UNLOAD))
 		ret = 1;
@@ -2379,7 +2379,7 @@ bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops)
     if (OPT_ISSET(ops,'u')) {
 	/* remove dependencies, which can't pertain to aliases */
 	const char *tnam = *args++;
-	node = find_module(tnam, 1, &tnam);
+	node = find_module(tnam, FINDMOD_ALIASP, &tnam);
 	if (!node)
 	    return 0;
 	m = (Module) getdata(node);
@@ -2443,133 +2443,6 @@ bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops)
     }
 }
 
-/* zmodload -ab */
-
-/**/
-static int
-bin_zmodload_auto(char *nam, char **args, Options ops)
-{
-    int ret = 0;
-    if (OPT_ISSET(ops,'u')) {
-	/* removed autoloaded builtins */
-	for (; *args; args++) {
-	    if (del_autobin(nam, *args, OPT_ISSET(ops,'i')))
-		ret = 1;
-	}
-	return ret;
-    } else if(!*args) {
-	/* list autoloaded builtins */
-	scanhashtable(builtintab, 1, 0, 0,
-	    autoloadscan, OPT_ISSET(ops,'L') ? PRINT_LIST : 0);
-	return 0;
-    } else {
-	/* add autoloaded builtins */
-	char *modnam;
-	modnam = *args++;
-	do {
-	    char *bnam = *args ? *args++ : modnam;
-	    if (add_autobin(nam, bnam, modnam, OPT_ISSET(ops,'i')))
-		ret = 1;
-	} while(*args);
-	return ret;
-    }
-}
-
-/* zmodload -ac */
-
-/**/
-static int
-bin_zmodload_cond(char *nam, char **args, Options ops)
-{
-    int ret = 0;
-
-    if (OPT_ISSET(ops,'u')) {
-	/* remove autoloaded conditions */
-	for (; *args; args++) {
-	    if (del_autocond(nam, *args, OPT_ISSET(ops,'I'),
-			     OPT_ISSET(ops,'i')))
-		ret = 1;
-	}
-	return ret;
-    } else if (!*args) {
-	/* list autoloaded conditions */
-	Conddef p;
-
-	for (p = condtab; p; p = p->next) {
-	    if (p->module) {
-		if (OPT_ISSET(ops,'L')) {
-		    fputs("zmodload -ac", stdout);
-		    if (p->flags & CONDF_INFIX)
-			putchar('I');
-		    printf(" %s %s\n", p->module, p->name);
-		} else {
-		    if (p->flags & CONDF_INFIX)
-			fputs("infix ", stdout);
-		    else
-			fputs("post ", stdout);
-		    printf("%s (%s)\n",p->name, p->module);
-		}
-	    }
-	}
-	return 0;
-    } else {
-	/* add autoloaded conditions */
-	char *modnam;
-
-	modnam = *args++;
-	do {
-	    char *cnam = *args ? *args++ : modnam;
-	    if (add_autocond(nam, cnam, OPT_ISSET(ops, 'I'),
-			     modnam, OPT_ISSET(ops,'i')))
-		ret = 1;
-	} while(*args);
-	return ret;
-    }
-}
-
-/* zmodload -af */
-
-/**/
-static int
-bin_zmodload_math(char *nam, char **args, Options ops)
-{
-    int ret = 0;
-
-    if (OPT_ISSET(ops,'u')) {
-	/* remove autoloaded math functions */
-	for (; *args; args++) {
-	    if (del_automathfunc(nam, *args, OPT_ISSET(ops,'i')))
-		ret = 1;
-	}
-	return ret;
-    } else if (!*args) {
-	/* list autoloaded math functions */
-	MathFunc p;
-
-	for (p = mathfuncs; p; p = p->next) {
-	    if (!(p->flags & MFF_USERFUNC) && p->module) {
-		if (OPT_ISSET(ops,'L')) {
-		    fputs("zmodload -af", stdout);
-		    printf(" %s %s\n", p->module, p->name);
-		} else
-		    printf("%s (%s)\n",p->name, p->module);
-	    }
-	}
-	return 0;
-    } else {
-	/* add autoloaded math functions */
-	char *modnam;
-
-	modnam = *args++;
-	do {
-	    char *fnam = *args ? *args++ : modnam;
-	    if (add_automathfunc(nam, fnam, modnam, OPT_ISSET(ops,'i')))
-		ret = 1;
-	} while(*args);
-	return ret;
-    }
-}
-
 /*
  * Function for scanning the parameter table to find and print
  * out autoloadable parameters.
@@ -2588,36 +2461,87 @@ printautoparams(HashNode hn, int lon)
     }
 }
 
-/* zmodload -ap */
+/* zmodload -a/u [bcpf] */
 
 /**/
 static int
-bin_zmodload_param(char *nam, char **args, Options ops)
+bin_zmodload_auto(char *nam, char **args, Options ops)
 {
-    int ret = 0;
+    int fchar;
+    char *modnam;
+
+    if (OPT_ISSET(ops,'c')) {
+	if (!*args) {
+	    /* list autoloaded conditions */
+	    Conddef p;
+
+	    for (p = condtab; p; p = p->next) {
+		if (p->module) {
+		    if (OPT_ISSET(ops,'L')) {
+			fputs("zmodload -ac", stdout);
+			if (p->flags & CONDF_INFIX)
+			    putchar('I');
+			printf(" %s %s\n", p->module, p->name);
+		    } else {
+			if (p->flags & CONDF_INFIX)
+			    fputs("infix ", stdout);
+			else
+			    fputs("post ", stdout);
+			printf("%s (%s)\n",p->name, p->module);
+		    }
+		}
+	    }
+	    return 0;
+	}
+	fchar = OPT_ISSET(ops,'I') ? 'C' : 'c';
+    } else if (OPT_ISSET(ops,'p')) {
+	if (!*args) {
+	    /* list autoloaded parameters */
+	    scanhashtable(paramtab, 1, 0, 0, printautoparams,
+			  OPT_ISSET(ops,'L'));
+	    return 0;
+	}
+	fchar = 'p';
+    } else if (OPT_ISSET(ops,'f')) {
+	if (!*args) {
+	    /* list autoloaded math functions */
+	    MathFunc p;
+
+	    for (p = mathfuncs; p; p = p->next) {
+		if (!(p->flags & MFF_USERFUNC) && p->module) {
+		    if (OPT_ISSET(ops,'L')) {
+			fputs("zmodload -af", stdout);
+			printf(" %s %s\n", p->module, p->name);
+		    } else
+			printf("%s (%s)\n",p->name, p->module);
+		}
+	    }
+	    return 0;
+	}
+	fchar = 'f';
+    } else {
+	/* builtins are the default; zmodload -ab or just zmodload -a */
+	if (!*args) {
+	    /* list autoloaded builtins */
+	    scanhashtable(builtintab, 1, 0, 0,
+			  autoloadscan, OPT_ISSET(ops,'L') ? PRINT_LIST : 0);
+	    return 0;
+	}
+	fchar = 'b';
+    }
 
     if (OPT_ISSET(ops,'u')) {
-	/* remove autoloaded parameters */
-	for (; *args; args++) {
-	    if (del_autoparam(nam, *args, OPT_ISSET(ops,'i')))
-		ret = 1;
-	}
-	return ret;
-    } else if (!*args) {
-	scanhashtable(paramtab, 1, 0, 0, printautoparams, OPT_ISSET(ops,'L'));
-	return 0;
+	/* remove autoload */
+	fchar *= -1;
+	modnam = NULL;
     } else {
-	/* add autoloaded parameters */
-	char *modnam;
+	/* add autoload */
+	modnam = *args;
 
-	modnam = *args++;
-	do {
-	    char *pnam = *args ? *args++ : modnam;
-	    if (add_autoparam(nam, pnam, modnam, OPT_ISSET(ops,'i')))
-		ret = 1;
-	} while(*args);
-	return ret;
+	if (args[1])
+	    args++;
     }
+    return autofeatures(nam, modnam, args, fchar, OPT_ISSET(ops,'i'));
 }
 
 /* Backend handler for zmodload -u */
@@ -2630,7 +2554,7 @@ unload_module(Module m, LinkNode node)
      * Only unload the real module, so resolve aliases.
      */
     if (m->flags & MOD_ALIAS) {
-	LinkNode node = find_module(m->u.alias, 1, NULL);
+	LinkNode node = find_module(m->u.alias, FINDMOD_ALIASP, NULL);
 	if (!node)
 	    return 1;
 	m = (Module) getdata(node);
@@ -2666,7 +2590,8 @@ unload_module(Module m, LinkNode node)
 	    LinkNode n;
 
 	    for (n = firstnode(m->deps); n; incnode(n)) {
-		LinkNode dn = find_module((char *) getdata(n), 1, NULL);
+		LinkNode dn = find_module((char *) getdata(n),
+					  FINDMOD_ALIASP, NULL);
 		Module dm;
 
 		if (dn && (dm = (Module) getdata(dn)) &&
@@ -2725,7 +2650,7 @@ unload_named_module(char *modname, char *nam, int silent)
     Module m;
     int ret = 0;
 
-    node = find_module(modname, 1, &mname);
+    node = find_module(modname, FINDMOD_ALIASP, &mname);
     if (node) {
 	LinkNode mn, dn;
 	int del = 0;
@@ -2832,7 +2757,7 @@ bin_zmodload_features(char *nam, char **args, Options ops)
 	int *enables = NULL, *ep;
 	char *param = OPT_ARG_SAFE(ops,'P');
 
-	node = find_module(modname, 1, NULL);
+	node = find_module(modname, FINDMOD_ALIASP, NULL);
 	if (node)
 	    m = ((Module) getdata(node));
 	if (!m || !m->u.handle || (m->flags & MOD_UNLOAD)) {
@@ -2949,7 +2874,7 @@ bin_zmodload_features(char *nam, char **args, Options ops)
 	zwarnnam(nam, "-P can only be used with -l or -L");
 	return 1;
     } else if (OPT_ISSET(ops,'a')) {
-	return autofeatures(nam, args, modname, OPT_ISSET(ops,'i'));
+	return autofeatures(nam, modname, args, 0, OPT_ISSET(ops,'i'));
     }
 
     return require_module(modname, args);
@@ -3115,64 +3040,106 @@ ensurefeature(char *modname, char *prefix, char *feature)
 
 /**/
 int
-autofeatures(char *cmdnam, char **features, char *module, int opt_i)
+autofeatures(char *cmdnam, char *module, char **features, int prefchar,
+	     int opt_i)
 {
-    int ret = 0, infix;
+    int ret = 0, subret;
+    int defflags = opt_i ? AUTOFEAT_IGNORE : 0;
 
     while (*features) {
-	char *feature = *features, *fnam;
-	int add;
-
-	if (*feature == '-') {
-	    add = 0;
-	    feature ++;
+	char *fnam, *typnam;
+	int add, fchar, flags = defflags;
+	autofeaturefn_t fn;
+
+	if (prefchar) {
+	    if (prefchar < 0) {
+		add = 0;
+		fchar = - prefchar;
+	    } else {
+		add = 1;
+		fchar = prefchar;
+	    }
+	    fnam = *features;
 	} else {
-	    add = 1;
-	    if (*feature == '+')
+	    char *feature = *features;
+	    if (*feature == '-') {
+		add = 0;
 		feature++;
-	}
+	    } else {
+		add = 1;
+		if (*feature == '+')
+		    feature++;
+	    }
 
-	if (!*feature || feature[1] != ':') {
-	    zwarnnam(cmdnam, "bad format for autoloadable feature: `%s'",
-		     feature);
-	    ret = 1;
+	    if (!*feature || feature[1] != ':') {
+		zwarnnam(cmdnam, "bad format for autoloadable feature: `%s'",
+			 feature);
+		ret = 1;
+	    }
+	    fnam = feature + 2;
+	    fchar = feature[0];
 	}
-	fnam = feature + 2;
-	switch (feature[0]) {
+
+	features++;
+
+	switch (fchar) {
 	case 'b':
-	    if (add ? add_autobin(cmdnam, fnam, module, opt_i) :
-		del_autobin(cmdnam, fnam, opt_i));
-		ret = 1;
+	    fn = add ? add_autobin : del_autobin;
+	    typnam = "builtin";
 	    break;
 
-	case 'c':
 	case 'C':
-	    infix = (feature[0] == 'C');
-	    if (add ? add_autocond(cmdnam, fnam, infix, module, opt_i) :
-		del_autocond(cmdnam, fnam, infix, opt_i))
-		ret = 1;
+	    flags |= AUTOFEAT_INFIX;
+	    /* FALLTHROUGH */
+	case 'c':
+	    fn = add ? add_autocond : del_autocond;
+	    typnam = "condition";
 	    break;
 
 	case 'p':
-	    if (add ? add_autoparam(cmdnam, fnam, module, opt_i) :
-		del_autoparam(cmdnam, fnam, opt_i));
-		ret = 1;
+	    fn = add ? add_autoparam : del_autoparam;
+	    typnam = "parameter";
 	    break;
 
 	case 'f':
-	    if (add ? add_automathfunc(cmdnam, fnam, module, opt_i) :
-		del_automathfunc(cmdnam, fnam, opt_i))
-		ret = 1;
+	    fn = add ? add_automathfunc : del_automathfunc;
+	    typnam = "math function";
 	    break;
 
 	default:
-	    zwarnnam(cmdnam, "bad autoloadable feature type: `%s'",
-		     feature);
+	    zwarnnam(cmdnam, "bad autoloadable feature type: `%c'",
+		     fchar);
 	    ret = 1;
-	    break;
+	    continue;
 	}
 
-	features++;
+	if (strchr(fnam, '/')) {
+	    zwarnnam(cmdnam, "%s: `/' is illegal in a %s", fnam, typnam);
+	    ret = 1;
+	    continue;
+	} 
+	subret = fn(module, fnam, flags);
+
+	if (subret != 0) {
+	    ret = 1;
+	    switch (subret) {
+	    case 1:
+		zwarnnam(cmdnam, "failed to add %s `%s'", typnam, fnam);
+		break;
+
+	    case 2:
+		zwarnnam(cmdnam, "%s: no such %s", fnam, typnam);
+		break;
+
+	    case 3:
+		zwarnnam(cmdnam, "%s: %s is already defined", fnam, typnam);
+		break;
+
+	    default:
+		/* no (further) message needed */
+		break;
+	    }
+	}
     }
 
     return ret;