summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/builtin.c2
-rw-r--r--Src/module.c306
-rw-r--r--Src/zsh.h2
3 files changed, 268 insertions, 42 deletions
diff --git a/Src/builtin.c b/Src/builtin.c
index 477e984cd..864fd41b9 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -123,7 +123,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, "ILabcfdipue", NULL),
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ARILabcfdipue", NULL),
     BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
 };
 
diff --git a/Src/module.c b/Src/module.c
index 2d8f6ce6f..bac8f5c42 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -88,6 +88,27 @@ register_module(char *n, Module_func setup, Module_func boot,
     zaddlinknode(linkedmodules, m);
 }
 
+/* Print an alias. */
+
+/**/
+static void
+printmodalias(Module m, char *ops)
+{
+    if (ops['L']) {
+	printf("zmodload -A ");
+	if (m->nam[0] == '-')
+	    fputs("-- ", stdout);
+	quotedzputs(m->nam, stdout);
+	putchar('=');
+	quotedzputs(m->u.alias, stdout);
+    } else {
+	nicezputs(m->nam, stdout);
+	fputs(" -> ", stdout);
+	nicezputs(m->u.alias, stdout);
+    }
+    putchar('\n');
+}
+
 /* Check if a module is linked in. */
 
 /**/
@@ -164,6 +185,15 @@ addwrapper(Module m, FuncWrap w)
 {
     FuncWrap p, q;
 
+    /*
+     * We can't add a wrapper to an alias, since it's supposed
+     * to behave identically to the resolved module.  This shouldn't
+     * happen since we usually add wrappers when a real module is
+     * loaded.
+     */
+    if (m->flags & MOD_ALIAS)
+	return 1;
+
     if (w->flags & WRAPF_ADDED)
 	return 1;
     for (p = wrappers, q = NULL; p; q = p, p = p->next);
@@ -256,6 +286,9 @@ deletewrapper(Module m, FuncWrap w)
 {
     FuncWrap p, q;
 
+    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);
 
@@ -292,7 +325,8 @@ load_and_bind(const char *fn)
 	int err = loadbind(0, (void *) addbuiltin, ret);
 	for (node = firstnode(modules); !err && node; incnode(node)) {
 	    Module m = (Module) getdata(node);
-	    if (m->u.handle && !(m->flags & MOD_LINKED))
+	    if (!(m->flags & MOD_ALIAS) &&
+		m->u.handle && !(m->flags & MOD_LINKED))
 		err |= loadbind(0, m->u.handle, ret);
 	}
 
@@ -431,21 +465,60 @@ do_load_module(char const *name)
 /**/
 #endif /* !DYNAMIC */
 
+/*
+ * Find a module in the list.
+ * If aliasp is non-zero, resolve any aliases to the underlying module.
+ * 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.
+ * Return NULL if the module named is not stored as a structure, or if we were
+ * resolving aliases and the final module named is not stored as a
+ * structure.
+ *
+ * TODO: now we have aliases, there may be some merit in using a hash
+ * table instead of a linked list.
+ */
 /**/
 static LinkNode
-find_module(const char *name)
+find_module(const char *name, int aliasp, const char **namep)
 {
     Module m;
     LinkNode node;
 
     for (node = firstnode(modules); node; incnode(node)) {
 	m = (Module) getdata(node);
-	if (!strcmp(m->nam, name))
+	if (!strcmp(m->nam, name)) {
+	    if (aliasp && (m->flags & MOD_ALIAS)) {
+		if (namep)
+		    *namep = m->u.alias;
+		return find_module(m->u.alias, 1, namep);
+	    }
+	    if (namep)
+		*namep = m->nam;
 	    return node;
+	}
     }
     return NULL;
 }
 
+/*
+ * Unlink and free a module node from the linked list.
+ */
+
+/**/
+static void
+delete_module(LinkNode node)
+{
+    Module m = (Module) remnode(modules, node);
+
+    if (m->flags & MOD_ALIAS)
+	zsfree(m->u.alias);
+    zsfree(m->nam);
+    if (m->deps)
+	freelinklist(m->deps, freestr);
+    zfree(m, sizeof(*m));
+}
+
 /**/
 mod_export int
 module_loaded(const char *name)
@@ -453,11 +526,17 @@ module_loaded(const char *name)
     LinkNode node;
     Module m;
 
-    return ((node = find_module(name)) &&
+    return ((node = find_module(name, 1, NULL)) &&
 	    (m = ((Module) getdata(node)))->u.handle &&
 	    !(m->flags & MOD_UNLOAD));
 }
 
+/*
+ * Setup and cleanup functions:  we don't search for aliases here,
+ * since they should have been resolved before we try to load or unload
+ * the module.
+ */
+
 /**/
 #ifdef DYNAMIC
 
@@ -679,7 +758,12 @@ load_module(char const *name)
 	zerr("invalid module name `%s'", name, 0);
 	return 0;
     }
-    if (!(node = find_module(name))) {
+    /*
+     * The following function call may alter name to the final name in a
+     * chain of aliases.  This makes sure the actual module loaded
+     * is the right one.
+     */
+    if (!(node = find_module(name, 1, &name))) {
 	if (!(linked = module_linked(name)) &&
 	    !(handle = do_load_module(name)))
 	    return 0;
@@ -697,10 +781,7 @@ load_module(char const *name)
 	if ((set = setup_module(m)) || boot_module(m)) {
 	    if (!set)
 		finish_module(m);
-	    remnode(modules, node);
-	    zsfree(m->nam);
-	    zfree(m, sizeof(*m));
-	    m->flags &= ~MOD_SETUP;
+	    delete_module(node);
 	    return 0;
 	}
 	m->flags |= MOD_INIT_S | MOD_INIT_B;
@@ -773,12 +854,13 @@ load_module(char const *name)
 
 /**/
 mod_export int
-require_module(char *nam, char *module, int res, int test)
+require_module(char *nam, const char *module, int res, int test)
 {
     Module m = NULL;
     LinkNode node;
 
-    node = find_module(module);
+    /* Resolve aliases and actual loadable module as for load_module */
+    node = find_module(module, 1, &module);
     if (node && (m = ((Module) getdata(node)))->u.handle &&
 	!(m->flags & MOD_UNLOAD)) {
 	if (test) {
@@ -793,12 +875,24 @@ require_module(char *nam, char *module, int res, int test)
 
 /**/
 void
-add_dep(char *name, char *from)
+add_dep(const char *name, char *from)
 {
     LinkNode node;
     Module m;
 
-    if (!(node = find_module(name))) {
+    /*
+     * If we were passed an alias, we must resolve it to a final
+     * module name (and maybe add the corresponding struct), since otherwise
+     * we would need to check all modules to see if they happen
+     * to be aliased to the same thing to implement depencies properly.
+     *
+     * This should mean that an attempt to add an alias which would
+     * have the same name as a module which has dependencies is correctly
+     * rejected, because then the module named already exists as a non-alias.
+     * 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 = zcalloc(sizeof(*m));
 	m->nam = ztrdup(name);
 	zaddlinknode(modules, m);
@@ -845,12 +939,21 @@ autoloadscan(HashNode hn, int printflags)
 int
 bin_zmodload(char *nam, char **args, char *ops, int func)
 {
-    if ((ops['b'] || ops['c'] || ops['p'] || ops['f']) &&
-	!(ops['a'] || ops['u'])) {
+    int ops_bcpf = ops['b'] || ops['c'] || ops['p'] || ops['f'];
+    int ops_au = ops['a'] || ops['u'];
+    if (ops_bcpf && !ops_au) {
 	zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u",
 		 NULL, 0);
 	return 1;
     }
+    if (ops['A'] || ops['R']) {
+	if (ops_bcpf || ops_au || ops['d'] || (ops['R'] && ops['e'])) {
+	    zwarnnam(nam, "illegal flags combined with -A or -R", NULL, 0);
+	    return 1;
+	}
+	if (!ops['e'])
+	    return bin_zmodload_alias(nam, args, ops);
+    }
     if (ops['d'] && ops['a']) {
 	zwarnnam(nam, "-d cannot be combined with -a", NULL, 0);
 	return 1;
@@ -886,32 +989,148 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
+bin_zmodload_alias(char *nam, char **args, char *ops)
+{
+    /*
+     * TODO: while it would be too nasty to have aliases, as opposed
+     * to real loadable modules, with dependencies --- just what would
+     * we need to load when, exactly? --- there is in principle no objection
+     * to making it possible to force an alias onto an existing unloaded
+     * module which has dependencies.  This would simply transfer
+     * the dependencies down the line to the aliased-to module name.
+     * This is actually useful, since then you can alias zsh/zle=mytestzle
+     * to load another version of zle.  But then what happens when the
+     * alias is removed?  Do you transfer the dependencies back? And
+     * suppose other names are aliased to the same file?  It might be
+     * kettle of fish best left unwormed.
+     */
+    LinkNode node;
+    Module m;
+    int ret = 0;
+
+    if (!*args) {
+	if (ops['R']) {
+	    zwarnnam(nam, "no module alias to remove", NULL, 0);
+	    return 1;
+	}
+	for (node = firstnode(modules); node; incnode(node)) {
+	    m = (Module) getdata(node);
+	    if (m->flags & MOD_ALIAS)
+		printmodalias(m, ops);
+	}
+	return 0;
+    }
+
+    for (; !ret && *args; args++) {
+	char *eqpos = strchr(*args, '=');
+	char *aliasname = eqpos ? eqpos+1 : NULL;
+	if (eqpos)
+	    *eqpos = '\0';
+	if (!modname_ok(*args)) {
+	    zwarnnam(nam, "invalid module name `%s'", *args, 0);
+	    return 1;
+	}
+	if (ops['R']) {
+	    if (aliasname) {
+		zwarnnam(nam, "bad syntax for removing module alias: %s",
+			 *args, 0);
+		return 1;
+	    }
+	    node = find_module(*args, 0, NULL);
+	    if (node) {
+		m = (Module) getdata(node);
+		if (!(m->flags & MOD_ALIAS)) {
+		    zwarnnam(nam, "module is not an alias: %s", *args, 0);
+		    ret = 1;
+		    break;
+		}
+		delete_module(node);
+	    } else {
+		zwarnnam(nam, "no such module alias: %s", *args, 0);
+		return 1;
+	    }
+	} else {
+	    if (aliasname) {
+		const char *mname = aliasname;
+		if (!modname_ok(aliasname)) {
+		    zwarnnam(nam, "invalid module name `%s'", aliasname, 0);
+		    return 1;
+		}
+		find_module(aliasname, 1, &mname);
+		if (!strcmp(mname, *args)) {
+		    zwarnnam(nam, "module alias would refer to itself: %s",
+			     *args, 0);
+		    return 1;
+		}
+		node = find_module(*args, 0, NULL);
+		if (node) {
+		    m = (Module) getdata(node);
+		    if (!(m->flags & MOD_ALIAS)) {
+			zwarnnam(nam, "module is not an alias: %s", *args, 0);
+			return 1;
+		    }
+		    zsfree(m->u.alias);
+		} else {
+		    m = (Module) zcalloc(sizeof(*m));
+		    m->nam = ztrdup(*args);
+		    m->flags = MOD_ALIAS;
+		    zaddlinknode(modules, m);
+		}
+		m->u.alias = ztrdup(aliasname);
+	    } else {
+		if ((node = find_module(*args, 0, NULL))) {
+		    m = (Module) getdata(node);
+		    if (m->flags & MOD_ALIAS)
+			printmodalias(m, ops);
+		    else {
+			zwarnnam(nam, "module is not an alias: %s",
+				 *args, 0);
+			return 1;
+		    }
+		} else {
+		    zwarnnam(nam, "no such module alias: %s", *args, 0);
+		    return 1;
+		}
+	    }
+	}
+    }
+
+    return 0;
+}
+
+/**/
+static int
 bin_zmodload_exist(char *nam, char **args, char *ops)
 {
     LinkNode node;
     Module m;
+    char *modname;
 
     if (!*args) {
 	for (node = firstnode(modules); node; incnode(node)) {
 	    m = (Module) getdata(node);
+	    modname = m->nam;
+	    if (m->flags & MOD_ALIAS) {
+		LinkNode node2;
+		if (ops['A'] && (node2 = find_module(m->u.alias, 1, NULL)))
+		    m = (Module) getdata(node2);
+		else
+		    continue;
+	    } 
 	    if (m->u.handle && !(m->flags & MOD_UNLOAD)) {
-		nicezputs(m->nam, stdout);
+		nicezputs(modname, stdout);
 		putchar('\n');
 	    }
 	}
 	return 0;
     } else {
-	int ret = 0, f;
+	int ret = 0;
 
 	for (; !ret && *args; args++) {
-	    f = 0;
-	    for (node = firstnode(modules);
-		 !f && node; incnode(node)) {
-		m = (Module) getdata(node);
-		if (m->u.handle && !(m->flags & MOD_UNLOAD))
-		    f = !strcmp(*args, m->nam);
-	    }
-	    ret = !f;
+	    if (!(node = find_module(*args, 1, NULL))
+		|| !(m = (Module) getdata(node))->u.handle
+		|| (m->flags & MOD_UNLOAD))
+		ret = 1;
 	}
 	return ret;
     }
@@ -924,9 +1143,9 @@ bin_zmodload_dep(char *nam, char **args, char *ops)
     LinkNode node;
     Module m;
     if(ops['u']) {
-	/* remove dependencies */
-	char *tnam = *args++;
-	node = find_module(tnam);
+	/* remove dependencies, which can't pertain to aliases */
+	const char *tnam = *args++;
+	node = find_module(tnam, 1, &tnam);
 	if (!node)
 	    return 0;
 	m = (Module) getdata(node);
@@ -949,11 +1168,8 @@ bin_zmodload_dep(char *nam, char **args, char *ops)
 		m->deps = NULL;
 	    }
 	}
-	if (!m->deps && !m->u.handle) {
-	    remnode(modules, node);
-	    zsfree(m->nam);
-	    zfree(m, sizeof(*m));
-	}
+	if (!m->deps && !m->u.handle)
+	    delete_module(node);
 	return 0;
     } else if(!args[0] || !args[1]) {
 	/* list dependencies */
@@ -1216,6 +1432,15 @@ bin_zmodload_param(char *nam, char **args, char *ops)
 int
 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);
+	if (!node)
+	    return 1;
+	m = (Module) getdata(node);
+    }
     if ((m->flags & MOD_INIT_S) &&
 	!(m->flags & MOD_UNLOAD) &&
 	((m->flags & MOD_LINKED) ?
@@ -1249,7 +1474,7 @@ unload_module(Module m, LinkNode node)
 	    LinkNode n;
 
 	    for (n = firstnode(m->deps); n; incnode(n)) {
-		LinkNode dn = find_module((char *) getdata(n));
+		LinkNode dn = find_module((char *) getdata(n), 1, NULL);
 		Module dm;
 
 		if (dn && (dm = (Module) getdata(dn)) &&
@@ -1287,9 +1512,7 @@ unload_module(Module m, LinkNode node)
 		if (!node)
 		    return 1;
 	    }
-	    remnode(modules, node);
-	    zsfree(m->nam);
-	    zfree(m, sizeof(*m));
+	    delete_module(node);
 	}
     }
     return 0;
@@ -1304,8 +1527,9 @@ bin_zmodload_load(char *nam, char **args, char *ops)
     int ret = 0;
     if(ops['u']) {
 	/* unload modules */
+	const char *mname = *args;
 	for(; *args; args++) {
-	    node = find_module(*args);
+	    node = find_module(*args, 1, &mname);
 	    if (node) {
 		LinkNode mn, dn;
 		int del = 0;
@@ -1314,11 +1538,11 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 		    m = (Module) getdata(mn);
 		    if (m->deps && m->u.handle)
 			for (dn = firstnode(m->deps); dn; incnode(dn))
-			    if (!strcmp((char *) getdata(dn), *args)) {
+			    if (!strcmp((char *) getdata(dn), mname)) {
 				if (m->flags & MOD_UNLOAD)
 				    del = 1;
 				else {
-				    zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", *args, 0);
+				    zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", mname, 0);
 				    ret = 1;
 				    goto cont;
 				}
@@ -1342,7 +1566,7 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 	/* list modules */
 	for (node = firstnode(modules); node; incnode(node)) {
 	    m = (Module) getdata(node);
-	    if (m->u.handle && !(m->flags & MOD_UNLOAD)) {
+	    if (m->u.handle && !(m->flags & (MOD_UNLOAD|MOD_ALIAS))) {
 		if(ops['L']) {
 		    printf("zmodload ");
 		    if(m->nam[0] == '-')
diff --git a/Src/zsh.h b/Src/zsh.h
index fe17d6cd6..bf59641b5 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -957,6 +957,7 @@ struct module {
     union {
 	void *handle;
 	Linkedmod linked;
+	char *alias;
     } u;
     LinkList deps;
     int wrapper;
@@ -968,6 +969,7 @@ struct module {
 #define MOD_LINKED  (1<<3)
 #define MOD_INIT_S  (1<<4)
 #define MOD_INIT_B  (1<<5)
+#define MOD_ALIAS   (1<<6)
 
 typedef int (*Module_func) _((Module));