summary refs log tree commit diff
path: root/Src/module.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/module.c')
-rw-r--r--Src/module.c651
1 files changed, 651 insertions, 0 deletions
diff --git a/Src/module.c b/Src/module.c
new file mode 100644
index 000000000..ce5989f07
--- /dev/null
+++ b/Src/module.c
@@ -0,0 +1,651 @@
+/*
+ * module.c - deal with dynamic modules
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1996-1997 Zoltán Hidvégi
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Zoltán Hidvégi or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Zoltán Hidvégi and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Zoltán Hidvégi and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Zoltán Hidvégi and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zsh.mdh"
+#include "module.pro"
+
+/* The `zsh' module contains all the base code that can't actually be built *
+ * as a separate module.  It is initialised by main(), so there's nothing   *
+ * for the boot function to do.                                             */
+
+/**/
+int
+boot_zsh(Module m)
+{
+    return 0;
+}
+
+/* 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   *
+ * builtin can be replaced using this function.                       */
+
+/**/
+int
+addbuiltin(Builtin b)
+{
+    Builtin bn = (Builtin) builtintab->getnode2(builtintab, b->nam);
+    if (bn && (bn->flags & BINF_ADDED))
+	return 1;
+    if (bn)
+	builtintab->freenode(builtintab->removenode(builtintab, b->nam));
+    PERMALLOC {
+	builtintab->addnode(builtintab, b->nam, b);
+    } LASTALLOC;
+    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)); */
+
+/**/
+int
+addbuiltins(char const *nam, Builtin binl, int size)
+{
+    int hads = 0, hadf = 0, n;
+
+    for(n = 0; n < size; n++) {
+	Builtin b = &binl[n];
+	if(b->flags & BINF_ADDED)
+	    continue;
+	if(addbuiltin(b)) {
+	    zwarnnam(nam, "name clash when adding builtin `%s'", b->nam, 0);
+	    hadf = 1;
+	} else {
+	    b->flags |= BINF_ADDED;
+	    hads = 2;
+	}
+    }
+    return hadf ? hads : 1;
+}
+
+#ifdef DYNAMIC
+
+/* $module_path ($MODULE_PATH) */
+
+/**/
+char **module_path;
+
+/* List of modules */
+
+/**/
+LinkList modules;
+
+/* 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 = zcalloc(sizeof(*bn));
+    bn->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((HashNode)bn);
+    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.           */
+
+/**/
+int
+deletebuiltins(char const *nam, Builtin binl, int size)
+{
+    int hads = 0, hadf = 0, n;
+
+    for(n = 0; n < size; n++) {
+	Builtin b = &binl[n];
+	if(!(b->flags & BINF_ADDED))
+	    continue;
+	if(deletebuiltin(b->nam)) {
+	    zwarnnam(nam, "builtin `%s' already deleted", b->nam, 0);
+	    hadf = 1;
+	} else
+	    hads = 2;
+	b->flags &= ~BINF_ADDED;
+    }
+    return hadf ? hads : 1;
+}
+
+#ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+#else
+# include <sys/types.h>
+# include <nlist.h>
+# include <link.h>
+#endif
+#ifndef RTLD_LAZY
+# define RTLD_LAZY 1
+#endif
+#ifndef RTLD_GLOBAL
+# define RTLD_GLOBAL 0
+#endif
+#ifndef HAVE_DLCLOSE
+# define dlclose(X) ((X), 0)
+#endif
+
+#ifdef DLSYM_NEEDS_UNDERSCORE
+# define STR_BOOT      "_boot_"
+# define STR_BOOT_S    "_boot_%s"
+# define STR_CLEANUP   "_cleanup_"
+# define STR_CLEANUP_S "_cleanup_%s"
+#else /* !DLSYM_NEEDS_UNDERSCORE */
+# define STR_BOOT      "boot_"
+# define STR_BOOT_S    "boot_%s"
+# define STR_CLEANUP   "cleanup_"
+# define STR_CLEANUP_S "cleanup_%s"
+#endif /* !DLSYM_NEEDS_UNDERSCORE */
+typedef int (*Module_func) _((Module));
+
+/**/
+static void *
+try_load_module(char const *name)
+{
+    char buf[PATH_MAX + 1];
+    char **pp;
+    void *ret = NULL;
+    int l;
+
+    if (strchr(name, '/')) {
+	ret = dlopen(unmeta(name), RTLD_LAZY | RTLD_GLOBAL);
+	if (ret || 
+	    unset(PATHDIRS) ||
+	    (*name == '/') ||
+	    (*name == '.' && name[1] == '/') ||
+	    (*name == '.' && name[1] == '.' && name[2] == '/'))
+	    return ret;
+    }
+
+    l = strlen(name) + 1;
+    for (pp = module_path; !ret && *pp; pp++) {
+	if (l + (**pp ? strlen(*pp) : 1) > PATH_MAX)
+	    continue;
+	sprintf(buf, "%s/%s", **pp ? *pp : ".", name);
+	ret = dlopen(unmeta(buf), RTLD_LAZY | RTLD_GLOBAL);
+    }
+
+    return ret;
+}
+
+/**/
+static void *
+do_load_module(char const *name)
+{
+    void *ret = NULL;
+    char buf[PATH_MAX + 1];
+
+    if (strlen(name) + strlen(DL_EXT) < PATH_MAX) {
+	sprintf(buf, "%s.%s", name, DL_EXT);
+	ret = try_load_module(buf);
+    }
+    if (!ret)
+	ret = try_load_module(name);
+    if (!ret) {
+	int waserr = errflag;
+	zerr("failed to load module: %s", name, 0);
+	errflag = waserr;
+    }
+    return ret;
+}
+
+/**/
+static LinkNode
+find_module(const char *name)
+{
+    Module m;
+    LinkNode node;
+
+    for (node = firstnode(modules); node; incnode(node)) {
+	m = (Module) getdata(node);
+	if (!strcmp(m->nam, name))
+	    return node;
+    }
+    return NULL;
+}
+
+/**/
+static int
+init_module(Module m)
+{
+    char *s, *t;
+#ifndef DYNAMIC_NAME_CLASH_OK
+    char buf[PATH_MAX + 1];
+#endif
+    Module_func fn;
+
+    s = strrchr(m->nam, '/');
+    if (s)
+	s = dupstring(++s);
+    else
+	s = m->nam;
+    if ((t = strrchr(s, '.')))
+	*t = '\0';
+#ifdef DYNAMIC_NAME_CLASH_OK
+    fn = (Module_func) dlsym(m->handle, STR_BOOT);
+#else /* !DYNAMIC_NAME_CLASH_OK */
+    if (strlen(s) + 6 > PATH_MAX)
+	return 1;
+    sprintf(buf, STR_BOOT_S, s);
+    fn = (Module_func) dlsym(m->handle, buf);
+#endif /* !DYNAMIC_NAME_CLASH_OK */
+    if(fn)
+	return fn(m);
+    zwarnnam(m->nam, "no boot function", NULL, 0);
+    return 1;
+}
+
+/**/
+Module
+load_module(char const *name)
+{
+    Module m;
+    void *handle;
+    LinkNode node, n;
+
+    if (!(node = find_module(name))) {
+	if (!(handle = do_load_module(name)))
+	    return NULL;
+	m = zcalloc(sizeof(*m));
+	m->nam = ztrdup(name);
+	m->handle = handle;
+	if (init_module(m)) {
+	    dlclose(handle);
+	    zsfree(m->nam);
+	    zfree(m, sizeof(*m));
+	    return NULL;
+	}
+	PERMALLOC {
+	    addlinknode(modules, m);
+	} LASTALLOC;
+	return m;
+    }
+    m = (Module) getdata(node);
+    if (m->handle)
+	return m;
+    if (m->flags & MOD_BUSY) {
+	zerr("circular dependencies for module %s", name, 0);
+	return NULL;
+    }
+    m->flags |= MOD_BUSY;
+    for (n = firstnode(m->deps); n; incnode(n))
+	if (!load_module((char *) getdata(n))) {
+	    m->flags &= ~MOD_BUSY;
+	    return NULL;
+	}
+    m->flags &= ~MOD_BUSY;
+    if (!(m->handle = do_load_module(name)))
+	return NULL;
+    if (init_module(m)) {
+	dlclose(m->handle);
+	m->handle = NULL;
+	return NULL;
+    }
+    return m;
+}
+
+/**/
+static int
+cleanup_module(Module m)
+{
+    char *s, *t;
+#ifndef DYNAMIC_NAME_CLASH_OK
+    char buf[PATH_MAX + 1];
+#endif
+    Module_func fn;
+
+    s = strrchr(m->nam, '/');
+    if (s)
+	s = dupstring(++s);
+    else
+	s = m->nam;
+    if ((t = strrchr(s, '.')))
+	*t = '\0';
+#ifdef DYNAMIC_NAME_CLASH_OK
+    fn = (Module_func) dlsym(m->handle, STR_CLEANUP);
+#else /* !DYNAMIC_NAME_CLASH_OK */
+    if (strlen(s) + 9 > PATH_MAX)
+	return 1;
+    sprintf(buf, STR_CLEANUP_S, s);
+    fn = (Module_func) dlsym(m->handle, buf);
+#endif /* !DYNAMIC_NAME_CLASH_OK */
+    if(fn)
+	return fn(m);
+    zwarnnam(m->nam, "no cleanup function", NULL, 0);
+    return 1;
+}
+
+/**/
+void
+add_dep(char *name, char *from)
+{
+    LinkNode node;
+    Module m;
+
+    PERMALLOC {
+	if (!(node = find_module(name))) {
+	    m = zcalloc(sizeof(*m));
+	    m->nam = ztrdup(name);
+	    addlinknode(modules, m);
+	} else
+	    m = (Module) getdata(node);
+	if (!m->deps)
+	    m->deps = newlinklist();
+	for (node = firstnode(m->deps);
+	     node && strcmp((char *) getdata(node), from);
+	     incnode(node));
+	if (!node)
+	    addlinknode(m->deps, ztrdup(from));
+    } LASTALLOC;
+}
+
+/**/
+static void
+autoloadscan(HashNode hn, int printflags)
+{
+    Builtin bn = (Builtin) hn;
+
+    if(bn->flags & BINF_ADDED)
+	return;
+    if(printflags & PRINT_LIST) {
+	fputs("zmodload -a ", stdout);
+	if(bn->optstr[0] == '-')
+	    fputs("-- ", stdout);
+	quotedzputs(bn->optstr, stdout);
+	if(strcmp(bn->nam, bn->optstr)) {
+	    putchar(' ');
+	    quotedzputs(bn->nam, stdout);
+	}
+    } else {
+	nicezputs(bn->nam, stdout);
+	if(strcmp(bn->nam, bn->optstr)) {
+	    fputs(" (", stdout);
+	    nicezputs(bn->optstr, stdout);
+	    putchar(')');
+	}
+    }
+    putchar('\n');
+}
+
+/**/
+int
+bin_zmodload(char *nam, char **args, char *ops, int func)
+{
+    if(ops['d'] && ops['a']) {
+	zwarnnam(nam, "-d cannot be combined with -a", NULL, 0);
+	return 1;
+    }
+    if (ops['u'] && !*args) {
+	zwarnnam(nam, "what do you want to unload?", NULL, 0);
+	return 1;
+    }
+    if(ops['d'])
+	return bin_zmodload_dep(nam, args, ops);
+    else if(ops['a'])
+	return bin_zmodload_auto(nam, args, ops);
+    else
+	return bin_zmodload_load(nam, args, ops);
+}
+
+/**/
+static int
+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);
+	if (!node)
+	    return 0;
+	m = (Module) getdata(node);
+	if(*args && m->deps) {
+	    do {
+		for(node = firstnode(m->deps); node; incnode(node))
+		    if(!strcmp(*args, getdata(node))) {
+			zsfree(getdata(node));
+			remnode(m->deps, node);
+			break;
+		    }
+	    } while(*++args);
+	    if(empty(m->deps)) {
+		freelinklist(m->deps, freestr);
+		m->deps = NULL;
+	    }
+	} else {
+	    if (m->deps) {
+		freelinklist(m->deps, freestr);
+		m->deps = NULL;
+	    }
+	}
+	if (!m->deps && !m->handle) {
+	    remnode(modules, node);
+	    zsfree(m->nam);
+	    zfree(m, sizeof(*m));
+	}
+	return 0;
+    } else if(!args[0] || !args[1]) {
+	/* list dependencies */
+	for (node = firstnode(modules); node; incnode(node)) {
+	    m = (Module) getdata(node);
+	    if (m->deps && (!args[0] || !strcmp(args[0], m->nam))) {
+		LinkNode n;
+		if(ops['L']) {
+		    printf("zmodload -d ");
+		    if(m->nam[0] == '-')
+			fputs("-- ", stdout);
+		    quotedzputs(m->nam, stdout);
+		} else {
+		    nicezputs(m->nam, stdout);
+		    putchar(':');
+		}
+		for (n = firstnode(m->deps); n; incnode(n)) {
+		    putchar(' ');
+		    if(ops['L'])
+			quotedzputs((char *) getdata(n), stdout);
+		    else
+			nicezputs((char *) getdata(n), stdout);
+		}
+		putchar('\n');
+	    }
+	}
+	return 0;
+    } else {
+	/* add dependencies */
+	int ret = 0;
+	char *tnam = *args++;
+
+	for(; *args; args++) {
+	    if(isset(RESTRICTED) && strchr(*args, '/')) {
+		zwarnnam(nam, "%s: restricted", *args, 0);
+		ret = 1;
+	    } else
+		add_dep(tnam, *args);
+	}
+	return ret;
+    }
+}
+
+/**/
+static int
+bin_zmodload_auto(char *nam, char **args, char *ops)
+{
+    int ret = 0;
+    if(ops['u']) {
+	/* remove autoloaded builtins */
+	for (; *args; args++) {
+	    Builtin bn = (Builtin) builtintab->getnode2(builtintab, *args);
+	    if (!bn) {
+		if(!ops['i']) {
+		    zwarnnam(nam, "%s: no such builtin", *args, 0);
+		    ret = 1;
+		}
+	    } else if (bn->flags & BINF_ADDED) {
+		zwarnnam(nam, "%s: builtin is already defined", *args, 0);
+		ret = 1;
+	    } else
+		deletebuiltin(*args);
+	}
+	return ret;
+    } else if(!*args) {
+	/* list autoloaded builtins */
+	scanhashtable(builtintab, 0, 0, 0,
+	    autoloadscan, ops['L'] ? PRINT_LIST : 0);
+	return 0;
+    } else {
+	/* add autoloaded builtins */
+	char *modnam;
+	modnam = *args++;
+	if(isset(RESTRICTED) && strchr(modnam, '/')) {
+	    zwarnnam(nam, "%s: restricted", modnam, 0);
+	    return 1;
+	}
+	do {
+	    char *bnam = *args ? *args++ : modnam;
+	    if (strchr(bnam, '/')) {
+		zwarnnam(nam, "%s: `/' is illegal in a builtin", bnam, 0);
+		ret = 1;
+	    } else if (add_autobin(bnam, modnam) && !ops['i']) {
+		zwarnnam(nam, "failed to add builtin %s", bnam, 0);
+		ret = 1;
+	    }
+	} while(*args);
+	return ret;
+    }
+}
+
+/**/
+static int
+bin_zmodload_load(char *nam, char **args, char *ops)
+{
+    LinkNode node;
+    Module m;
+    int ret = 0;
+    if(ops['u']) {
+	/* unload modules */
+	for(; *args; args++) {
+	    node = find_module(*args);
+	    if (node) {
+		LinkNode mn, dn;
+
+		for (mn = firstnode(modules); mn; incnode(mn)) {
+		    m = (Module) getdata(mn);
+		    if (m->deps && m->handle)
+			for (dn = firstnode(m->deps); dn; incnode(dn))
+			    if (!strcmp((char *) getdata(dn), *args)) {
+				zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", *args, 0);
+				ret = 1;
+				goto cont;
+			    }
+		}
+
+		m = (Module) getdata(node);
+		if (m->handle && cleanup_module(m))
+		    ret = 1;
+		else {
+		    if (m->handle)
+			dlclose(m->handle);
+		    m->handle = NULL;
+		    if(!m->deps) {
+			remnode(modules, node);
+			zsfree(m->nam);
+			zfree(m, sizeof(*m));
+		    }
+		}
+	    } else if (!ops['i']) {
+		zwarnnam(nam, "no such module %s", *args, 0);
+		ret = 1;
+	    }
+	    cont: ;
+	}
+	return ret;
+    } else if(!*args) {
+	/* list modules */
+	for (node = firstnode(modules); node; incnode(node)) {
+	    m = (Module) getdata(node);
+	    if (m->handle) {
+		if(ops['L']) {
+		    printf("zmodload ");
+		    if(m->nam[0] == '-')
+			fputs("-- ", stdout);
+		    quotedzputs(m->nam, stdout);
+		} else
+		    nicezputs(m->nam, stdout);
+		putchar('\n');
+	    }
+	}
+	return 0;
+    } else {
+	/* load modules */
+	for (; *args; args++) {
+	    node = find_module(*args);
+	    if (node && ((Module) getdata(node))->handle) {
+		if (!ops['i']) {
+		    zwarnnam(nam, "module %s already loaded.", *args, 0);
+		    ret = 1;
+		}
+	    } else if (isset(RESTRICTED) && strchr(*args, '/')) {
+		zwarnnam(nam, "%s: restricted", *args, 0);
+		ret = 1;
+	    } else if (!load_module(*args))
+		ret = 1;
+	}
+	return ret;
+    }
+}
+
+#endif /* DYNAMIC */