--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/openenvutils/commandshell/shell/src/module.c Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,2227 @@
+// module.c - deal with dynamic modules
+//
+// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved.
+//
+/*
+ * 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"
+
+#ifdef __SYMBIAN32__
+#ifdef __WINSCW__
+#pragma warn_unusedarg off
+#pragma warn_possunwant off
+#endif//__WINSCW__
+#endif//__SYMBIAN32__
+
+/* List of linked-in modules. */
+
+/**/
+LinkList linkedmodules;
+
+
+/* The `zsh/main' module contains all the base code that can't actually be *
+ * built as a separate module. It is initialised by main(), so there's *
+ * nothing for the boot function to do. */
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+ return 0;
+}
+
+/**/
+int
+boot_(UNUSED(Module m))
+{
+ return 0;
+}
+
+/**/
+int
+cleanup_(UNUSED(Module m))
+{
+ return 0;
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+ return 0;
+}
+
+/* This registers a builtin module. */
+
+/**/
+void
+register_module(char *n, Module_func setup, Module_func boot,
+ Module_func cleanup, Module_func finish)
+{
+ Linkedmod m;
+
+ m = (Linkedmod) zalloc(sizeof(*m));
+
+ m->name = ztrdup(n);
+ m->setup = setup;
+ m->boot = boot;
+ m->cleanup = cleanup;
+ m->finish = finish;
+
+ zaddlinknode(linkedmodules, m);
+}
+
+/* Print an alias. */
+
+/**/
+static void
+printmodalias(Module m, Options ops)
+{
+ if (OPT_ISSET(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. */
+
+/**/
+Linkedmod
+module_linked(char const *name)
+{
+ LinkNode node;
+
+ for (node = firstnode(linkedmodules); node; incnode(node))
+ if (!strcmp(((Linkedmod) getdata(node))->name, name))
+ return (Linkedmod) getdata(node);
+
+ return NULL;
+}
+
+/* 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));
+ builtintab->addnode(builtintab, b->nam, b);
+ return 0;
+}
+
+/* Add multiple builtins. binl points to a table of `size' builtin *
+ * structures. Those for which (.flags & BINF_ADDED) is false are to be *
+ * added; that flag is set if they succeed. If any fail, an error *
+ * message is printed, using nam as the leading name. Returns 1 if all *
+ * additions succeed, 2 if some succeed and some fail, and 0 if all (and *
+ * at least 1) fail. The usual usage in a boot_*() function would be *
+ * return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); */
+
+/**/
+mod_export 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;
+}
+
+/* The list of function wrappers defined. */
+
+/**/
+FuncWrap wrappers;
+
+/* This adds a definition for a wrapper. Return value is one in case of *
+ * error and zero if all went fine. */
+
+/**/
+mod_export int
+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);
+ if (q)
+ q->next = w;
+ else
+ wrappers = w;
+ w->next = NULL;
+ w->flags |= WRAPF_ADDED;
+ w->module = m;
+
+ return 0;
+}
+
+/* $module_path ($MODULE_PATH) */
+
+/**/
+char **module_path;
+
+/* List of modules */
+
+/**/
+mod_export 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 = zshcalloc(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. */
+
+/**/
+mod_export 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;
+}
+
+/* This removes the given wrapper definition from the list. Returned is *
+ * one in case of error and zero otherwise. */
+
+/**/
+mod_export int
+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);
+
+ if (p) {
+ if (q)
+ q->next = p->next;
+ else
+ wrappers = p->next;
+ p->flags &= ~WRAPF_ADDED;
+
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**/
+#ifdef DYNAMIC
+
+/**/
+#ifdef AIXDYNAMIC
+
+#include <sys/ldr.h>
+
+static char *dlerrstr[256];
+
+static void *
+load_and_bind(const char *fn)
+{
+ void *ret = (void *) load((char *) fn, L_NOAUTODEFER, NULL);
+
+ if (ret) {
+ LinkNode node;
+ int err = loadbind(0, (void *) addbuiltin, ret);
+ for (node = firstnode(modules); !err && node; incnode(node)) {
+ Module m = (Module) getdata(node);
+ if (!(m->flags & MOD_ALIAS) &&
+ m->u.handle && !(m->flags & MOD_LINKED))
+ err |= loadbind(0, m->u.handle, ret);
+ }
+
+ if (err) {
+ loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr));
+ unload(ret);
+ ret = NULL;
+ }
+ } else
+ loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr));
+
+ return ret;
+}
+
+#define dlopen(X,Y) load_and_bind(X)
+#define dlclose(X) unload(X)
+#define dlerror() (dlerrstr[0])
+
+/**/
+#else
+
+#ifdef HAVE_DLFCN_H
+# if defined(HAVE_DL_H) && defined(HPUXDYNAMIC)
+# include <dl.h>
+# else
+# include <dlfcn.h>
+# endif
+#else
+# ifdef HAVE_DL_H
+# include <dl.h>
+# define RTLD_LAZY BIND_DEFERRED
+# define RTLD_GLOBAL DYNAMIC_PATH
+# else
+# include <sys/types.h>
+# include <nlist.h>
+# include <link.h>
+# endif
+#endif
+
+/**/
+#ifdef HPUXDYNAMIC
+# define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+# define dlclose(handle) shl_unload((shl_t)(handle))
+
+static
+void *
+hpux_dlsym(void *handle, char *name)
+{
+ void *sym_addr;
+ if (!shl_findsym((shl_t *)&handle, name, TYPE_UNDEFINED, &sym_addr))
+ return sym_addr;
+ return NULL;
+}
+
+# define dlsym(handle,name) hpux_dlsym(handle,name)
+# define dlerror() 0
+#else
+# ifndef HAVE_DLCLOSE
+# define dlclose(X) ((X), 0)
+# endif
+/**/
+#endif
+
+#ifdef DLSYM_NEEDS_UNDERSCORE
+# define STR_SETUP "_setup_"
+# define STR_BOOT "_boot_"
+# define STR_CLEANUP "_cleanup_"
+# define STR_FINISH "_finish_"
+#else /* !DLSYM_NEEDS_UNDERSCORE */
+# define STR_SETUP "setup_"
+# define STR_BOOT "boot_"
+# define STR_CLEANUP "cleanup_"
+# define STR_FINISH "finish_"
+#endif /* !DLSYM_NEEDS_UNDERSCORE */
+
+/**/
+#endif /* !AIXDYNAMIC */
+
+#ifndef RTLD_LAZY
+# define RTLD_LAZY 1
+#endif
+#ifndef RTLD_GLOBAL
+# define RTLD_GLOBAL 0
+#endif
+
+/**/
+static void *
+try_load_module(char const *name)
+{
+ char buf[PATH_MAX + 1];
+ char **pp;
+ void *ret = NULL;
+ int l;
+
+ l = 1 + strlen(name) + 1 + strlen(DL_EXT);
+ for (pp = module_path; !ret && *pp; pp++) {
+ if (l + (**pp ? strlen(*pp) : 1) > PATH_MAX)
+ continue;
+ sprintf(buf, "%s/%s.%s", **pp ? *pp : ".", name, DL_EXT);
+ ret = dlopen(unmeta(buf), RTLD_LAZY | RTLD_GLOBAL);
+ }
+
+ return ret;
+}
+
+/**/
+static void *
+do_load_module(char const *name)
+{
+ void *ret;
+
+ ret = try_load_module(name);
+ if (!ret) {
+ int waserr = errflag;
+ zerr("failed to load module: %s", name, 0);
+ errflag = waserr;
+ }
+ return ret;
+}
+
+/**/
+#else /* !DYNAMIC */
+
+/**/
+static void *
+do_load_module(char const *name)
+{
+ int waserr = errflag;
+
+ zerr("failed to load module: %s", name, 0);
+ errflag = waserr;
+
+ return NULL;
+}
+
+/**/
+#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, 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 (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)
+{
+ LinkNode node;
+ Module m;
+
+ 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
+
+/**/
+#ifdef AIXDYNAMIC
+
+/**/
+static int
+dyn_setup_module(Module m)
+{
+ return ((int (*)_((int,Module))) m->u.handle)(0, m);
+}
+
+/**/
+static int
+dyn_boot_module(Module m)
+{
+ return ((int (*)_((int,Module))) m->u.handle)(1, m);
+}
+
+/**/
+static int
+dyn_cleanup_module(Module m)
+{
+ return ((int (*)_((int,Module))) m->u.handle)(2, m);
+}
+
+/**/
+static int
+dyn_finish_module(Module m)
+{
+ return ((int (*)_((int,Module))) m->u.handle)(3, m);
+}
+
+/**/
+#else
+
+static Module_func
+module_func(Module m, char *name)
+{
+#ifdef DYNAMIC_NAME_CLASH_OK
+ return (Module_func) dlsym(m->u.handle, name);
+#else /* !DYNAMIC_NAME_CLASH_OK */
+ VARARR(char, buf, strlen(name) + strlen(m->nam)*2 + 1);
+ char const *p;
+ char *q;
+ strcpy(buf, name);
+ q = strchr(buf, 0);
+ for(p = m->nam; *p; p++) {
+ if(*p == '/') {
+ *q++ = 'Q';
+ *q++ = 's';
+ } else if(*p == '_') {
+ *q++ = 'Q';
+ *q++ = 'u';
+ } else if(*p == 'Q') {
+ *q++ = 'Q';
+ *q++ = 'q';
+ } else
+ *q++ = *p;
+ }
+ *q = 0;
+ return (Module_func) dlsym(m->u.handle, buf);
+#endif /* !DYNAMIC_NAME_CLASH_OK */
+}
+
+/**/
+static int
+dyn_setup_module(Module m)
+{
+ Module_func fn = module_func(m, STR_SETUP);
+
+ if (fn)
+ return fn(m);
+ zwarnnam(m->nam, "no setup function", NULL, 0);
+ return 1;
+}
+
+/**/
+static int
+dyn_boot_module(Module m)
+{
+ Module_func fn = module_func(m, STR_BOOT);
+
+ if(fn)
+ return fn(m);
+ zwarnnam(m->nam, "no boot function", NULL, 0);
+ return 1;
+}
+
+/**/
+static int
+dyn_cleanup_module(Module m)
+{
+ Module_func fn = module_func(m, STR_CLEANUP);
+
+ if(fn)
+ return fn(m);
+ zwarnnam(m->nam, "no cleanup function", NULL, 0);
+ return 1;
+}
+
+/* Note that this function does more than just calling finish_foo(), *
+ * it really unloads the module. */
+
+/**/
+static int
+dyn_finish_module(Module m)
+{
+ Module_func fn = module_func(m, STR_FINISH);
+ int r;
+
+ if (fn)
+ r = fn(m);
+ else {
+ zwarnnam(m->nam, "no finish function", NULL, 0);
+ r = 1;
+ }
+ dlclose(m->u.handle);
+ return r;
+}
+
+/**/
+#endif /* !AIXDYNAMIC */
+
+/**/
+static int
+setup_module(Module m)
+{
+ return ((m->flags & MOD_LINKED) ?
+ (m->u.linked->setup)(m) : dyn_setup_module(m));
+}
+
+/**/
+static int
+boot_module(Module m)
+{
+ return ((m->flags & MOD_LINKED) ?
+ (m->u.linked->boot)(m) : dyn_boot_module(m));
+}
+
+/**/
+static int
+cleanup_module(Module m)
+{
+ return ((m->flags & MOD_LINKED) ?
+ (m->u.linked->cleanup)(m) : dyn_cleanup_module(m));
+}
+
+/**/
+static int
+finish_module(Module m)
+{
+ return ((m->flags & MOD_LINKED) ?
+ (m->u.linked->finish)(m) : dyn_finish_module(m));
+}
+
+/**/
+#else /* !DYNAMIC */
+
+/**/
+static int
+setup_module(Module m)
+{
+ return ((m->flags & MOD_LINKED) ? (m->u.linked->setup)(m) : 1);
+}
+
+/**/
+static int
+boot_module(Module m)
+{
+ return ((m->flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1);
+}
+
+/**/
+static int
+cleanup_module(Module m)
+{
+ return ((m->flags & MOD_LINKED) ? (m->u.linked->cleanup)(m) : 1);
+}
+
+/**/
+static int
+finish_module(Module m)
+{
+ return ((m->flags & MOD_LINKED) ? (m->u.linked->finish)(m) : 1);
+}
+
+/**/
+#endif /* !DYNAMIC */
+
+/**/
+static int
+modname_ok(char const *p)
+{
+ do {
+ if(*p != '_' && !ialnum(*p))
+ return 0;
+ do {
+ p++;
+ } while(*p == '_' || ialnum(*p));
+ if(!*p)
+ return 1;
+ } while(*p++ == '/');
+ return 0;
+}
+
+/**/
+mod_export int
+load_module(char const *name)
+{
+ Module m;
+ void *handle = NULL;
+ Linkedmod linked;
+ LinkNode node, n;
+ int set;
+
+ if (!modname_ok(name)) {
+ zerr("invalid module name `%s'", name, 0);
+ return 0;
+ }
+ /*
+ * 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.
+ */
+ queue_signals();
+ if (!(node = find_module(name, 1, &name))) {
+ if (!(linked = module_linked(name)) &&
+ !(handle = do_load_module(name))) {
+ unqueue_signals();
+ return 0;
+ }
+ m = zshcalloc(sizeof(*m));
+ m->nam = ztrdup(name);
+ if (handle) {
+ m->u.handle = handle;
+ m->flags |= MOD_SETUP;
+ } else {
+ m->u.linked = linked;
+ m->flags |= MOD_SETUP | MOD_LINKED;
+ }
+ node = zaddlinknode(modules, m);
+
+ if ((set = setup_module(m)) || boot_module(m)) {
+ if (!set)
+ finish_module(m);
+ delete_module(node);
+ unqueue_signals();
+ return 0;
+ }
+ m->flags |= MOD_INIT_S | MOD_INIT_B;
+ m->flags &= ~MOD_SETUP;
+ unqueue_signals();
+ return 1;
+ }
+ m = (Module) getdata(node);
+ if (m->flags & MOD_SETUP) {
+ unqueue_signals();
+ return 1;
+ }
+ if (m->flags & MOD_UNLOAD)
+ m->flags &= ~MOD_UNLOAD;
+ else if ((m->flags & MOD_LINKED) ? m->u.linked : m->u.handle) {
+ unqueue_signals();
+ return 1;
+ }
+ if (m->flags & MOD_BUSY) {
+ zerr("circular dependencies for module %s", name, 0);
+ return 0;
+ }
+ m->flags |= MOD_BUSY;
+ if (m->deps)
+ for (n = firstnode(m->deps); n; incnode(n))
+ if (!load_module((char *) getdata(n))) {
+ m->flags &= ~MOD_BUSY;
+ unqueue_signals();
+ return 0;
+ }
+ m->flags &= ~MOD_BUSY;
+ if (!m->u.handle) {
+ handle = NULL;
+ if (!(linked = module_linked(name)) &&
+ !(handle = do_load_module(name))) {
+ unqueue_signals();
+ return 0;
+ }
+ if (handle) {
+ m->u.handle = handle;
+ m->flags |= MOD_SETUP;
+ } else {
+ m->u.linked = linked;
+ m->flags |= MOD_SETUP | MOD_LINKED;
+ }
+ if (setup_module(m)) {
+ if (handle)
+ m->u.handle = NULL;
+ else
+ m->u.linked = NULL;
+ m->flags &= ~MOD_SETUP;
+ unqueue_signals();
+ return 0;
+ }
+ m->flags |= MOD_INIT_S;
+ }
+ m->flags |= MOD_SETUP;
+ if (boot_module(m)) {
+ finish_module(m);
+ if (m->flags & MOD_LINKED)
+ m->u.linked = NULL;
+ else
+ m->u.handle = NULL;
+ m->flags &= ~MOD_SETUP;
+ unqueue_signals();
+ return 0;
+ }
+ m->flags |= MOD_INIT_B;
+ m->flags &= ~MOD_SETUP;
+ unqueue_signals();
+ return 1;
+}
+
+/* This ensures that the module with the name given as the second argument
+ * is loaded.
+ * The third argument should be non-zero if the function should complain
+ * about trying to load a module with a full path name in restricted mode.
+ * The last argument should be non-zero if this function should signal an
+ * error if the module is already loaded.
+ * The return value is non-zero if the module was found or loaded. */
+
+/**/
+mod_export int
+require_module(char *nam, const char *module, UNUSED(int res), int test)
+{
+ Module m = NULL;
+ LinkNode node;
+ int ret = 1;
+
+ /* Resolve aliases and actual loadable module as for load_module */
+ queue_signals();
+ node = find_module(module, 1, &module);
+ if (node && (m = ((Module) getdata(node)))->u.handle &&
+ !(m->flags & MOD_UNLOAD)) {
+ if (test) {
+ unqueue_signals();
+ zwarnnam(nam, "module %s already loaded.", module, 0);
+ return 0;
+ }
+ } else
+ ret = load_module(module);
+ unqueue_signals();
+
+ return ret;
+}
+
+/**/
+void
+add_dep(const char *name, char *from)
+{
+ LinkNode node;
+ Module m;
+
+ /*
+ * 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 dependencies 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 = zshcalloc(sizeof(*m));
+ m->nam = ztrdup(name);
+ zaddlinknode(modules, m);
+ } else
+ m = (Module) getdata(node);
+ if (!m->deps)
+ m->deps = znewlinklist();
+ for (node = firstnode(m->deps);
+ node && strcmp((char *) getdata(node), from);
+ incnode(node));
+ if (!node)
+ zaddlinknode(m->deps, ztrdup(from));
+}
+
+/**/
+static void
+autoloadscan(HashNode hn, int printflags)
+{
+ Builtin bn = (Builtin) hn;
+
+ if(bn->flags & BINF_ADDED)
+ return;
+ if(printflags & PRINT_LIST) {
+ fputs("zmodload -ab ", 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');
+}
+
+#ifndef __SYMBIAN32__
+/**/
+int
+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;
+
+ if (ops_bcpf && !ops_au) {
+ zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u",
+ NULL, 0);
+ return 1;
+ }
+ if (OPT_ISSET(ops,'A') || OPT_ISSET(ops,'R')) {
+ if (ops_bcpf || ops_au || OPT_ISSET(ops,'d') ||
+ (OPT_ISSET(ops,'R') && OPT_ISSET(ops,'e'))) {
+ zwarnnam(nam, "illegal flags combined with -A or -R", NULL, 0);
+ return 1;
+ }
+ if (!OPT_ISSET(ops,'e'))
+ return bin_zmodload_alias(nam, args, ops);
+ }
+ if (OPT_ISSET(ops,'d') && OPT_ISSET(ops,'a')) {
+ zwarnnam(nam, "-d cannot be combined with -a", NULL, 0);
+ return 1;
+ }
+ if (OPT_ISSET(ops,'u') && !*args) {
+ zwarnnam(nam, "what do you want to unload?", NULL, 0);
+ return 1;
+ }
+ if (OPT_ISSET(ops,'e') && (OPT_ISSET(ops,'I') || OPT_ISSET(ops,'L') ||
+ OPT_ISSET(ops,'a') || OPT_ISSET(ops,'d') ||
+ OPT_ISSET(ops,'i') || OPT_ISSET(ops,'u'))) {
+ zwarnnam(nam, "-e cannot be combined with other options", NULL, 0);
+ return 1;
+ }
+ queue_signals();
+ if (OPT_ISSET(ops,'e'))
+ ret = bin_zmodload_exist(nam, args, ops);
+ else if (OPT_ISSET(ops,'d'))
+ ret = bin_zmodload_dep(nam, args, ops);
+ 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')))
+ ret = bin_zmodload_load(nam, args, ops);
+ else
+ zwarnnam(nam, "use only one of -b, -c, or -p", NULL, 0);
+ unqueue_signals();
+
+ return ret;
+}
+#endif //#ifndef __SYMBIAN32__
+
+/**/
+static int
+bin_zmodload_alias(char *nam, char **args, Options 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 (OPT_ISSET(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 (OPT_ISSET(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) zshcalloc(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(UNUSED(char *nam), char **args, Options 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 (OPT_ISSET(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(modname, stdout);
+ putchar('\n');
+ }
+ }
+ return 0;
+ } else {
+ int ret = 0;
+
+ for (; !ret && *args; args++) {
+ if (!(node = find_module(*args, 1, NULL))
+ || !(m = (Module) getdata(node))->u.handle
+ || (m->flags & MOD_UNLOAD))
+ ret = 1;
+ }
+ return ret;
+ }
+}
+
+/**/
+static int
+bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops)
+{
+ LinkNode node;
+ Module m;
+ if (OPT_ISSET(ops,'u')) {
+ /* 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);
+ if (*args && m->deps) {
+ do {
+ LinkNode dnode;
+ for (dnode = firstnode(m->deps); dnode; incnode(dnode))
+ if (!strcmp(*args, getdata(dnode))) {
+ zsfree(getdata(dnode));
+ remnode(m->deps, dnode);
+ 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->u.handle)
+ delete_module(node);
+ 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 (OPT_ISSET(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(OPT_ISSET(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++)
+ add_dep(tnam, *args);
+ return ret;
+ }
+}
+
+/**/
+static int
+bin_zmodload_auto(char *nam, char **args, Options ops)
+{
+ int ret = 0;
+ if(OPT_ISSET(ops,'u')) {
+ /* remove autoloaded builtins */
+ for (; *args; args++) {
+ Builtin bn = (Builtin) builtintab->getnode2(builtintab, *args);
+ if (!bn) {
+ if(!OPT_ISSET(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, OPT_ISSET(ops,'L') ? PRINT_LIST : 0);
+ return 0;
+ } else {
+ /* add autoloaded builtins */
+ char *modnam;
+ modnam = *args++;
+ 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) && !OPT_ISSET(ops,'i')) {
+ zwarnnam(nam, "failed to add builtin %s", bnam, 0);
+ ret = 1;
+ }
+ } while(*args);
+ return ret;
+ }
+}
+
+/**/
+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++) {
+ Conddef cd = getconddef(OPT_ISSET(ops,'I'), *args, 0);
+
+ if (!cd) {
+ if (!OPT_ISSET(ops,'i')) {
+ zwarnnam(nam, "%s: no such condition", *args, 0);
+ ret = 1;
+ }
+ } else if (cd->flags & CONDF_ADDED) {
+ zwarnnam(nam, "%s: condition is already defined", *args, 0);
+ ret = 1;
+ } else
+ deleteconddef(cd);
+ }
+ 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 (strchr(cnam, '/')) {
+ zwarnnam(nam, "%s: `/' is illegal in a condition", cnam, 0);
+ ret = 1;
+ } else if (add_autocond(cnam, OPT_ISSET(ops,'I'), modnam) &&
+ !OPT_ISSET(ops,'i')) {
+ zwarnnam(nam, "failed to add condition `%s'", cnam, 0);
+ ret = 1;
+ }
+ } while(*args);
+ return ret;
+ }
+}
+
+/**/
+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++) {
+ MathFunc f = getmathfunc(*args, 0);
+
+ if (!f) {
+ if (!OPT_ISSET(ops,'i')) {
+ zwarnnam(nam, "%s: no such math function", *args, 0);
+ ret = 1;
+ }
+ } else if (f->flags & MFF_ADDED) {
+ zwarnnam(nam, "%s: math function is already defined", *args, 0);
+ ret = 1;
+ } else
+ deletemathfunc(f);
+ }
+ return ret;
+ } else if (!*args) {
+ /* list autoloaded math functions */
+ MathFunc p;
+
+ for (p = mathfuncs; p; p = p->next) {
+ if (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 (strchr(fnam, '/')) {
+ zwarnnam(nam, "%s: `/' is illegal in a math function",
+ fnam, 0);
+ ret = 1;
+ } else if (add_automathfunc(fnam, modnam) && !OPT_ISSET(ops,'i')) {
+ zwarnnam(nam, "failed to add math function `%s'", fnam, 0);
+ ret = 1;
+ }
+ } while(*args);
+ return ret;
+ }
+}
+
+static void
+printautoparams(HashNode hn, int lon)
+{
+ Param pm = (Param) hn;
+
+ if (pm->flags & PM_AUTOLOAD) {
+ if (lon)
+ printf("zmodload -ap %s %s\n", pm->u.str, pm->nam);
+ else
+ printf("%s (%s)\n", pm->nam, pm->u.str);
+ }
+}
+
+/**/
+static int
+bin_zmodload_param(char *nam, char **args, Options ops)
+{
+ int ret = 0;
+
+ if (OPT_ISSET(ops,'u')) {
+ /* remove autoloaded parameters */
+ for (; *args; args++) {
+ Param pm = (Param) gethashnode2(paramtab, *args);
+
+ if (!pm) {
+ if (!OPT_ISSET(ops,'i')) {
+ zwarnnam(nam, "%s: no such parameter", *args, 0);
+ ret = 1;
+ }
+ } else if (!(pm->flags & PM_AUTOLOAD)) {
+ zwarnnam(nam, "%s: parameter is already defined", *args, 0);
+ ret = 1;
+ } else
+ unsetparam_pm(pm, 0, 1);
+ }
+ return ret;
+ } else if (!*args) {
+ scanhashtable(paramtab, 1, 0, 0, printautoparams, OPT_ISSET(ops,'L'));
+ return 0;
+ } else {
+ /* add autoloaded parameters */
+ char *modnam;
+
+ modnam = *args++;
+ do {
+ char *pnam = *args ? *args++ : modnam;
+ if (strchr(pnam, '/')) {
+ zwarnnam(nam, "%s: `/' is illegal in a parameter", pnam, 0);
+ ret = 1;
+ } else
+ add_autoparam(pnam, modnam);
+ } while(*args);
+ return ret;
+ }
+}
+
+/**/
+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) ?
+ (m->u.linked && m->u.linked->cleanup(m)) :
+ (m->u.handle && cleanup_module(m))))
+ return 1;
+ else {
+ int del = (m->flags & MOD_UNLOAD);
+
+ if (m->wrapper) {
+ m->flags |= MOD_UNLOAD;
+ return 0;
+ }
+ m->flags &= ~MOD_UNLOAD;
+ if (m->flags & MOD_INIT_B) {
+ if (m->flags & MOD_LINKED) {
+ if (m->u.linked) {
+ m->u.linked->finish(m);
+ m->u.linked = NULL;
+ }
+ } else {
+ if (m->u.handle) {
+ finish_module(m);
+ m->u.handle = NULL;
+ }
+ }
+ }
+ if (del && m->deps) {
+ /* The module was unloaded delayed, unload all modules *
+ * on which it depended. */
+ LinkNode n;
+
+ for (n = firstnode(m->deps); n; incnode(n)) {
+ LinkNode dn = find_module((char *) getdata(n), 1, NULL);
+ Module dm;
+
+ if (dn && (dm = (Module) getdata(dn)) &&
+ (dm->flags & MOD_UNLOAD)) {
+ /* See if this is the only module depending on it. */
+
+ LinkNode an;
+ Module am;
+ int du = 1;
+
+ for (an = firstnode(modules); du && an; incnode(an)) {
+ am = (Module) getdata(an);
+ if (am != m && am->deps &&
+ ((am->flags & MOD_LINKED) ?
+ am->u.linked : am->u.handle)) {
+ LinkNode sn;
+
+ for (sn = firstnode(am->deps); du && sn;
+ incnode(sn)) {
+ if (!strcmp((char *) getdata(sn), dm->nam))
+ du = 0;
+ }
+ }
+ }
+ if (du)
+ unload_module(dm, NULL);
+ }
+ }
+ }
+ if(!m->deps) {
+ if (!node) {
+ for (node = firstnode(modules); node; incnode(node))
+ if (m == (Module) getdata(node))
+ break;
+ if (!node)
+ return 1;
+ }
+ delete_module(node);
+ }
+ }
+ return 0;
+}
+
+/**/
+static int
+bin_zmodload_load(char *nam, char **args, Options ops)
+{
+ LinkNode node;
+ Module m;
+ int ret = 0;
+ if(OPT_ISSET(ops,'u')) {
+ /* unload modules */
+ const char *mname = *args;
+ for(; *args; args++) {
+ node = find_module(*args, 1, &mname);
+ if (node) {
+ LinkNode mn, dn;
+ int del = 0;
+
+ for (mn = firstnode(modules); mn; incnode(mn)) {
+ m = (Module) getdata(mn);
+ if (m->deps && m->u.handle)
+ for (dn = firstnode(m->deps); dn; incnode(dn))
+ 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", mname, 0);
+ ret = 1;
+ goto cont;
+ }
+ }
+ }
+ m = (Module) getdata(node);
+ if (del)
+ m->wrapper++;
+ if (unload_module(m, node))
+ ret = 1;
+ if (del)
+ m->wrapper--;
+ } else if (!OPT_ISSET(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->u.handle && !(m->flags & (MOD_UNLOAD|MOD_ALIAS))) {
+ if(OPT_ISSET(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++)
+ if (!require_module(nam, *args, 1, (!OPT_ISSET(ops,'i'))))
+ ret = 1;
+
+ return ret;
+ }
+}
+
+/* The list of module-defined conditions. */
+
+/**/
+mod_export Conddef condtab;
+
+/* This gets a condition definition with the given name. The first *
+ * argument says if we have to look for an infix condition. The last *
+ * argument is non-zero if we should autoload modules if needed. */
+
+/**/
+Conddef
+getconddef(int inf, char *name, int autol)
+{
+ Conddef p;
+ int f = 1;
+
+ do {
+ for (p = condtab; p; p = p->next) {
+ if ((!!inf == !!(p->flags & CONDF_INFIX)) &&
+ !strcmp(name, p->name))
+ break;
+ }
+ if (autol && p && p->module) {
+ /* This is a definition for an autoloaded condition, load the *
+ * module if we haven't tried that already. */
+ if (f) {
+ load_module(p->module);
+ f = 0;
+ p = NULL;
+ } else {
+ deleteconddef(p);
+ return NULL;
+ }
+ } else
+ break;
+ } while (!p);
+ return p;
+}
+
+/* This adds the given condition definition. The return value is zero on *
+ * success and 1 on failure. If there is a matching definition for an *
+ * autoloaded condition, it is removed. */
+
+/**/
+int
+addconddef(Conddef c)
+{
+ Conddef p = getconddef((c->flags & CONDF_INFIX), c->name, 0);
+
+ if (p) {
+ if (!p->module || (p->flags & CONDF_ADDED))
+ return 1;
+ /* There is an autoload definition. */
+
+ deleteconddef(p);
+ }
+ c->next = condtab;
+ condtab = c;
+ return 0;
+}
+
+/* This adds multiple condition definitions. This is like addbuiltins(). */
+
+/**/
+mod_export int
+addconddefs(char const *nam, Conddef c, int size)
+{
+ int hads = 0, hadf = 0;
+
+ while (size--) {
+ if (c->flags & CONDF_ADDED) {
+ c++;
+ continue;
+ }
+ if (addconddef(c)) {
+ zwarnnam(nam, "name clash when adding condition `%s'", c->name, 0);
+ hadf = 1;
+ } else {
+ c->flags |= CONDF_ADDED;
+ hads = 2;
+ }
+ c++;
+ }
+ return hadf ? hads : 1;
+}
+
+/* This list of hook functions defined. */
+
+/**/
+Hookdef hooktab;
+
+/* Find a hook definition given the name. */
+
+/**/
+Hookdef
+gethookdef(char *n)
+{
+ Hookdef p;
+
+ for (p = hooktab; p; p = p->next)
+ if (!strcmp(n, p->name))
+ return p;
+ return NULL;
+}
+
+/* This adds the given hook definition. The return value is zero on *
+ * success and 1 on failure. */
+
+/**/
+int
+addhookdef(Hookdef h)
+{
+ if (gethookdef(h->name))
+ return 1;
+
+ h->next = hooktab;
+ hooktab = h;
+ h->funcs = znewlinklist();
+
+ return 0;
+}
+
+/* This adds multiple hook definitions. This is like addbuiltins(). */
+
+/**/
+mod_export int
+addhookdefs(char const *nam, Hookdef h, int size)
+{
+ int hads = 0, hadf = 0;
+
+ while (size--) {
+ if (addhookdef(h)) {
+ zwarnnam(nam, "name clash when adding hook `%s'", h->name, 0);
+ hadf = 1;
+ } else
+ hads = 2;
+ h++;
+ }
+ return hadf ? hads : 1;
+}
+
+/* Delete hook definitions. */
+
+/**/
+int
+deletehookdef(Hookdef h)
+{
+ Hookdef p, q;
+
+ for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next);
+
+ if (!p)
+ return 1;
+
+ if (q)
+ q->next = p->next;
+ else
+ hooktab = p->next;
+ freelinklist(p->funcs, NULL);
+ return 0;
+}
+
+/**/
+mod_export int
+deletehookdefs(UNUSED(char const *nam), Hookdef h, int size)
+{
+ while (size--) {
+ deletehookdef(h);
+ h++;
+ }
+ return 1;
+}
+
+/* Add a function to a hook. */
+
+/**/
+int
+addhookdeffunc(Hookdef h, Hookfn f)
+{
+ zaddlinknode(h->funcs, (void *) f);
+
+ return 0;
+}
+
+/**/
+mod_export int
+addhookfunc(char *n, Hookfn f)
+{
+ Hookdef h = gethookdef(n);
+
+ if (h)
+ return addhookdeffunc(h, f);
+ return 1;
+}
+
+/* Delete a function from a hook. */
+
+/**/
+int
+deletehookdeffunc(Hookdef h, Hookfn f)
+{
+ LinkNode p;
+
+ for (p = firstnode(h->funcs); p; incnode(p))
+ if (f == (Hookfn) getdata(p)) {
+ remnode(h->funcs, p);
+ return 0;
+ }
+ return 1;
+}
+
+/**/
+mod_export int
+deletehookfunc(char *n, Hookfn f)
+{
+ Hookdef h = gethookdef(n);
+
+ if (h)
+ return deletehookdeffunc(h, f);
+ return 1;
+}
+
+/* Run the function(s) for a hook. */
+
+/**/
+mod_export int
+runhookdef(Hookdef h, void *d)
+{
+ if (empty(h->funcs)) {
+ if (h->def)
+ return h->def(h, d);
+ return 0;
+ } else if (h->flags & HOOKF_ALL) {
+ LinkNode p;
+ int r;
+
+ for (p = firstnode(h->funcs); p; incnode(p))
+ if ((r = ((Hookfn) getdata(p))(h, d)))
+ return r;
+ if (h->def)
+ return h->def(h, d);
+ return 0;
+ } else
+ return ((Hookfn) getdata(lastnode(h->funcs)))(h, d);
+}
+
+/**/
+int
+runhook(char *n, void *d)
+{
+ Hookdef h = gethookdef(n);
+
+ if (h)
+ return runhookdef(h, d);
+ return 0;
+}
+
+/* This adds the given parameter definition. The return value is zero on *
+ * success and 1 on failure. */
+
+/**/
+int
+addparamdef(Paramdef d)
+{
+ Param pm;
+
+ if ((pm = (Param) gethashnode2(paramtab, d->name)))
+ unsetparam_pm(pm, 0, 1);
+
+ if (!(pm = createparam(d->name, d->flags)) &&
+ !(pm = (Param) paramtab->getnode(paramtab, d->name)))
+ return 1;
+
+ pm->level = 0;
+ pm->u.data = d->var;
+ if (d->gsu)
+ pm->gsu.i = (GsuInteger) d->gsu;
+ else {
+ /*
+ * If no get/set/unset class, use the appropriate
+ * variable type.
+ */
+ switch (PM_TYPE(pm->flags)) {
+ case PM_SCALAR:
+ pm->gsu.s = &varscalar_gsu;
+ break;
+
+ case PM_INTEGER:
+ pm->gsu.i = &varinteger_gsu;
+ break;
+
+ case PM_ARRAY:
+ pm->gsu.a = &vararray_gsu;
+ break;
+
+ default:
+ unsetparam_pm(pm, 0, 1);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* This adds multiple parameter definitions. This is like addbuiltins(). */
+
+/**/
+mod_export int
+addparamdefs(char const *nam, Paramdef d, int size)
+{
+ int hads = 0, hadf = 0;
+
+ while (size--) {
+ if (addparamdef(d)) {
+ zwarnnam(nam, "error when adding parameter `%s'", d->name, 0);
+ hadf = 1;
+ } else
+ hads = 2;
+ d++;
+ }
+ return hadf ? hads : 1;
+}
+
+/* Delete parameters defined. No error checking yet. */
+
+/**/
+int
+deleteparamdef(Paramdef d)
+{
+ unsetparam(d->name);
+ return 0;
+}
+
+/**/
+mod_export int
+deleteparamdefs(UNUSED(char const *nam), Paramdef d, int size)
+{
+ while (size--) {
+ deleteparamdef(d);
+ d++;
+ }
+ return 1;
+}
+
+/* This adds a definition for autoloading a module for a condition. */
+
+/**/
+int
+add_autocond(char *nam, int inf, char *module)
+{
+ Conddef c = (Conddef) zalloc(sizeof(*c));
+
+ c->name = ztrdup(nam);
+ c->flags = (inf ? CONDF_INFIX : 0);
+ c->module = ztrdup(module);
+
+ if (addconddef(c)) {
+ zsfree(c->name);
+ zsfree(c->module);
+ zfree(c, sizeof(*c));
+
+ return 1;
+ }
+ return 0;
+}
+
+/* This removes the given condition definition from the list(s). If this *
+ * is a definition for a autoloaded condition, the memory is freed. */
+
+/**/
+int
+deleteconddef(Conddef c)
+{
+ Conddef p, q;
+
+ for (p = condtab, q = NULL; p && p != c; q = p, p = p->next);
+
+ if (p) {
+ if (q)
+ q->next = p->next;
+ else
+ condtab = p->next;
+
+ if (p->module) {
+ /* autoloaded, free it */
+ zsfree(p->name);
+ zsfree(p->module);
+ zfree(p, sizeof(*p));
+ }
+ return 0;
+ }
+ return -1;
+}
+
+/* This removes multiple condition definitions (like deletebuiltins()). */
+
+/**/
+mod_export int
+deleteconddefs(char const *nam, Conddef c, int size)
+{
+ int hads = 0, hadf = 0;
+
+ while (size--) {
+ if (!(c->flags & CONDF_ADDED)) {
+ c++;
+ continue;
+ }
+ if (deleteconddef(c)) {
+ zwarnnam(nam, "condition `%s' already deleted", c->name, 0);
+ hadf = 1;
+ } else
+ hads = 2;
+ c->flags &= ~CONDF_ADDED;
+ c++;
+ }
+ return hadf ? hads : 1;
+}
+
+/* This adds a definition for autoloading a module for a parameter. */
+
+/**/
+void
+add_autoparam(char *nam, char *module)
+{
+ Param pm;
+
+ queue_signals();
+ if ((pm = (Param) gethashnode2(paramtab, nam)))
+ unsetparam_pm(pm, 0, 1);
+
+ pm = setsparam(nam, ztrdup(module));
+
+ pm->flags |= PM_AUTOLOAD;
+ unqueue_signals();
+}
+
+/* List of math functions. */
+
+/**/
+MathFunc mathfuncs;
+
+/**/
+static void removemathfunc(MathFunc previous, MathFunc current)
+{
+ if (previous)
+ previous->next = current->next;
+ else
+ mathfuncs = current->next;
+
+ zsfree(current->name);
+ zsfree(current->module);
+ zfree(current, sizeof(*current));
+}
+
+/**/
+MathFunc
+getmathfunc(char *name, int autol)
+{
+ MathFunc p, q = NULL;
+
+ for (p = mathfuncs; p; q = p, p = p->next)
+ if (!strcmp(name, p->name)) {
+ if (autol && p->module) {
+ char *n = dupstring(p->module);
+
+ removemathfunc(q, p);
+
+ load_module(n);
+
+ return getmathfunc(name, 0);
+ }
+ return p;
+ }
+
+ return NULL;
+}
+
+/**/
+mod_export int
+addmathfunc(MathFunc f)
+{
+ MathFunc p, q = NULL;
+
+ if (f->flags & MFF_ADDED)
+ return 1;
+
+ for (p = mathfuncs; p; q = p, p = p->next)
+ if (!strcmp(f->name, p->name)) {
+ if (p->module) {
+ /*
+ * Autoloadable, replace.
+ */
+ removemathfunc(q, p);
+ break;
+ }
+ return 1;
+ }
+
+ f->flags |= MFF_ADDED;
+ f->next = mathfuncs;
+ mathfuncs = f;
+
+ return 0;
+}
+
+/**/
+mod_export int
+addmathfuncs(char const *nam, MathFunc f, int size)
+{
+ int hads = 0, hadf = 0;
+
+ while (size--) {
+ if (f->flags & MFF_ADDED) {
+ f++;
+ continue;
+ }
+ if (addmathfunc(f)) {
+ zwarnnam(nam, "name clash when adding math function `%s'",
+ f->name, 0);
+ hadf = 1;
+ } else
+ hads = 2;
+ f++;
+ }
+ return hadf ? hads : 1;
+}
+
+/**/
+int
+add_automathfunc(char *nam, char *module)
+{
+ MathFunc f = (MathFunc) zalloc(sizeof(*f));
+
+ f->name = ztrdup(nam);
+ f->module = ztrdup(module);
+ f->flags = 0;
+
+ if (addmathfunc(f)) {
+ zsfree(f->name);
+ zsfree(f->module);
+ zfree(f, sizeof(*f));
+
+ return 1;
+ }
+ f->flags &= ~MFF_ADDED; /* still to autoload, not added yet */
+
+ return 0;
+}
+
+/**/
+mod_export int
+deletemathfunc(MathFunc f)
+{
+ MathFunc p, q;
+
+ for (p = mathfuncs, q = NULL; p && p != f; q = p, p = p->next);
+
+ if (p) {
+ if (q)
+ q->next = f->next;
+ else
+ mathfuncs = f->next;
+
+ if (f->module) {
+ zsfree(f->name);
+ zsfree(f->module);
+ zfree(f, sizeof(*f));
+ } else
+ f->flags &= ~MFF_ADDED;
+
+ return 0;
+ }
+ return -1;
+}
+
+/**/
+mod_export int
+deletemathfuncs(char const *nam, MathFunc f, int size)
+{
+ int hads = 0, hadf = 0;
+
+ while (size--) {
+ if (!(f->flags & MFF_ADDED)) {
+ f++;
+ continue;
+ }
+ if (deletemathfunc(f)) {
+ zwarnnam(nam, "math function `%s' already deleted",
+ f->name, 0);
+ hadf = 1;
+ } else
+ hads = 2;
+ f++;
+ }
+ return hadf ? hads : 1;
+}