summaryrefslogtreecommitdiff
path: root/dotfiles/system/.zsh/modules/Src/module.c
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-05-08 18:49:34 -0500
committerCraig Jennings <c@cjennings.net>2025-05-08 18:51:59 -0500
commit000e00871830cd15de032c80e2b62946cf19445c (patch)
tree794a7922750472bbe0e024042d6ba84f411fc3e0 /dotfiles/system/.zsh/modules/Src/module.c
parentfe302606931e4bad91c4ed6df81a4403523ba780 (diff)
adding missing dotfiles and folders
- profile.d/ - bashrc - authinfo.gpg - .zsh/
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/module.c')
-rw-r--r--dotfiles/system/.zsh/modules/Src/module.c3641
1 files changed, 3641 insertions, 0 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/module.c b/dotfiles/system/.zsh/modules/Src/module.c
new file mode 100644
index 0000000..4ae7831
--- /dev/null
+++ b/dotfiles/system/.zsh/modules/Src/module.c
@@ -0,0 +1,3641 @@
+/*
+ * 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"
+
+/*
+ * List of linked-in modules.
+ * This is set up at boot and remains for the life of the shell;
+ * entries do not appear in "zmodload" listings.
+ */
+
+/**/
+LinkList linkedmodules;
+
+/* $module_path ($MODULE_PATH) */
+
+/**/
+char **module_path;
+
+/* Hash of modules */
+
+/**/
+mod_export HashTable modulestab;
+
+/*
+ * Bit flags passed as the "flags" argument of a autofeaturefn_t.
+ * Used in other places, such as the final argument to
+ * do_module_features().
+ */
+enum {
+ /*
+ * `-i' option: ignore errors pertaining to redefinitions,
+ * or indicate to do_module_features() that it should be
+ * silent.
+ */
+ FEAT_IGNORE = 0x0001,
+ /* If a condition, condition is infix rather than prefix */
+ FEAT_INFIX = 0x0002,
+ /*
+ * Enable all features in the module when autoloading.
+ * This is the traditional zmodload -a behaviour;
+ * zmodload -Fa only enables features explicitly marked for
+ * autoloading.
+ */
+ FEAT_AUTOALL = 0x0004,
+ /*
+ * Remove feature: alternative to "-X:NAME" used if
+ * X is passed separately from NAME.
+ */
+ FEAT_REMOVE = 0x0008,
+ /*
+ * For do_module_features(). Check that any autoloads
+ * for the module are actually provided.
+ */
+ FEAT_CHECKAUTO = 0x0010
+};
+
+/*
+ * 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, -1 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)(const char *module, const char *feature,
+ int flags);
+
+/* 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,
+};
+
+static void
+freemodulenode(HashNode hn)
+{
+ Module m = (Module) hn;
+
+ if (m->node.flags & MOD_ALIAS)
+ zsfree(m->u.alias);
+ zsfree(m->node.nam);
+ if (m->autoloads)
+ freelinklist(m->autoloads, freestr);
+ if (m->deps)
+ freelinklist(m->deps, freestr);
+ zfree(m, sizeof(*m));
+}
+
+/* flags argument to printmodulenode */
+enum {
+ /* -L flag, output zmodload commands */
+ PRINTMOD_LIST = 0x0001,
+ /* -e flag */
+ PRINTMOD_EXIST = 0x0002,
+ /* -A flag */
+ PRINTMOD_ALIAS = 0x0004,
+ /* -d flag */
+ PRINTMOD_DEPS = 0x0008,
+ /* -F flag */
+ PRINTMOD_FEATURES = 0x0010,
+ /* -l flag in combination with -L flag */
+ PRINTMOD_LISTALL = 0x0020,
+ /* -a flag */
+ PRINTMOD_AUTO = 0x0040
+};
+
+/* Scan function for printing module details */
+
+static void
+printmodulenode(HashNode hn, int flags)
+{
+ Module m = (Module)hn;
+ /*
+ * If we check for a module loaded under an alias, we
+ * need the name of the alias. We can use it in other
+ * cases, too.
+ */
+ const char *modname = m->node.nam;
+
+ if (flags & PRINTMOD_DEPS) {
+ /*
+ * Print the module's dependencies.
+ */
+ LinkNode n;
+
+ if (!m->deps)
+ return;
+
+ if (flags & PRINTMOD_LIST) {
+ printf("zmodload -d ");
+ if (modname[0] == '-')
+ fputs("-- ", stdout);
+ quotedzputs(modname, stdout);
+ } else {
+ nicezputs(modname, stdout);
+ putchar(':');
+ }
+ for (n = firstnode(m->deps); n; incnode(n)) {
+ putchar(' ');
+ if (flags & PRINTMOD_LIST)
+ quotedzputs((char *) getdata(n), stdout);
+ else
+ nicezputs((char *) getdata(n), stdout);
+ }
+ } else if (flags & PRINTMOD_EXIST) {
+ /*
+ * Just print the module name, provided the module is
+ * present under an alias or otherwise.
+ */
+ if (m->node.flags & MOD_ALIAS) {
+ if (!(flags & PRINTMOD_ALIAS) ||
+ !(m = find_module(m->u.alias, FINDMOD_ALIASP, NULL)))
+ return;
+ }
+ if (!m->u.handle || (m->node.flags & MOD_UNLOAD))
+ return;
+ nicezputs(modname, stdout);
+ } else if (m->node.flags & MOD_ALIAS) {
+ /*
+ * Normal listing, but for aliases.
+ */
+ if (flags & PRINTMOD_LIST) {
+ printf("zmodload -A ");
+ if (modname[0] == '-')
+ fputs("-- ", stdout);
+ quotedzputs(modname, stdout);
+ putchar('=');
+ quotedzputs(m->u.alias, stdout);
+ } else {
+ nicezputs(modname, stdout);
+ fputs(" -> ", stdout);
+ nicezputs(m->u.alias, stdout);
+ }
+ } else if (m->u.handle || (flags & PRINTMOD_AUTO)) {
+ /*
+ * Loaded module.
+ */
+ if (flags & PRINTMOD_LIST) {
+ /*
+ * List with -L format. Possibly we are printing
+ * features, either enables or autoloads.
+ */
+ char **features = NULL;
+ int *enables = NULL;
+ if (flags & PRINTMOD_AUTO) {
+ if (!m->autoloads || !firstnode(m->autoloads))
+ return;
+ } else if (flags & PRINTMOD_FEATURES) {
+ if (features_module(m, &features) ||
+ enables_module(m, &enables) ||
+ !*features)
+ return;
+ }
+ printf("zmodload ");
+ if (flags & PRINTMOD_AUTO) {
+ fputs("-Fa ", stdout);
+ } else if (features)
+ fputs("-F ", stdout);
+ if(modname[0] == '-')
+ fputs("-- ", stdout);
+ quotedzputs(modname, stdout);
+ if (flags & PRINTMOD_AUTO) {
+ LinkNode an;
+ for (an = firstnode(m->autoloads); an; incnode(an)) {
+ putchar(' ');
+ quotedzputs((char *)getdata(an), stdout);
+ }
+ } else if (features) {
+ const char *f;
+ while ((f = *features++)) {
+ int on = *enables++;
+ if (flags & PRINTMOD_LISTALL)
+ printf(" %s", on ? "+" : "-");
+ else if (!on)
+ continue;
+ else
+ putchar(' ');
+ quotedzputs(f, stdout);
+ }
+ }
+ } else /* -l */
+ nicezputs(modname, stdout);
+ } else
+ return;
+ putchar('\n');
+}
+
+/**/
+HashTable
+newmoduletable(int size, char const *name)
+{
+ HashTable ht;
+ ht = newhashtable(size, name, NULL);
+
+ ht->hash = hasher;
+ ht->emptytable = emptyhashtable;
+ ht->filltable = NULL;
+ ht->cmpnodes = strcmp;
+ ht->addnode = addhashnode;
+ /* DISABLED is not supported */
+ ht->getnode = gethashnode2;
+ ht->getnode2 = gethashnode2;
+ ht->removenode = removehashnode;
+ ht->disablenode = NULL;
+ ht->enablenode = NULL;
+ ht->freenode = freemodulenode;
+ ht->printnode = printmodulenode;
+
+ return ht;
+}
+
+/************************************************************************
+ * zsh/main standard module functions
+ ************************************************************************/
+
+/* 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
+features_(UNUSED(Module m), UNUSED(char ***features))
+{
+ /*
+ * There are lots and lots of features, but they're not
+ * handled here.
+ */
+ return 1;
+}
+
+/**/
+int
+enables_(UNUSED(Module m), UNUSED(int **enables))
+{
+ return 1;
+}
+
+/**/
+int
+boot_(UNUSED(Module m))
+{
+ return 0;
+}
+
+/**/
+int
+cleanup_(UNUSED(Module m))
+{
+ return 0;
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+ return 0;
+}
+
+
+/************************************************************************
+ * Module utility functions
+ ************************************************************************/
+
+/* This registers a builtin module. */
+
+/**/
+void
+register_module(char *n, Module_void_func setup,
+ Module_features_func features,
+ Module_enables_func enables,
+ Module_void_func boot,
+ Module_void_func cleanup,
+ Module_void_func finish)
+{
+ Linkedmod m;
+
+ m = (Linkedmod) zalloc(sizeof(*m));
+
+ m->name = ztrdup(n);
+ m->setup = setup;
+ m->features = features;
+ m->enables = enables;
+ m->boot = boot;
+ m->cleanup = cleanup;
+ m->finish = finish;
+
+ zaddlinknode(linkedmodules, m);
+}
+
+/* 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;
+}
+
+
+/************************************************************************
+ * Support for the various feature types.
+ * First, builtins.
+ ************************************************************************/
+
+/* 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. */
+
+/**/
+static int
+addbuiltin(Builtin b)
+{
+ Builtin bn = (Builtin) builtintab->getnode2(builtintab, b->node.nam);
+ if (bn && (bn->node.flags & BINF_ADDED))
+ return 1;
+ if (bn)
+ builtintab->freenode(builtintab->removenode(builtintab, b->node.nam));
+ builtintab->addnode(builtintab, b->node.nam, b);
+ return 0;
+}
+
+/* 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. */
+
+/**/
+static int
+add_autobin(const char *module, const char *bnam, int flags)
+{
+ Builtin bn;
+ int ret;
+
+ bn = zshcalloc(sizeof(*bn));
+ bn->node.nam = ztrdup(bnam);
+ bn->optstr = ztrdup(module);
+ if (flags & FEAT_AUTOALL)
+ bn->node.flags |= BINF_AUTOALL;
+ if ((ret = addbuiltin(bn))) {
+ builtintab->freenode(&bn->node);
+ if (!(flags & FEAT_IGNORE))
+ return 1;
+ }
+ return 0;
+}
+
+/* Remove the builtin added previously by addbuiltin(). Returns *
+ * zero on succes and -1 if there is no builtin with that name. */
+
+/**/
+int
+deletebuiltin(const char *nam)
+{
+ Builtin bn;
+
+ bn = (Builtin) builtintab->removenode(builtintab, nam);
+ if (!bn)
+ return -1;
+ builtintab->freenode(&bn->node);
+ return 0;
+}
+
+/* Remove an autoloaded added by add_autobin */
+
+/**/
+static int
+del_autobin(UNUSED(const char *module), const char *bnam, int flags)
+{
+ Builtin bn = (Builtin) builtintab->getnode2(builtintab, bnam);
+ if (!bn) {
+ if(!(flags & FEAT_IGNORE))
+ return 2;
+ } else if (bn->node.flags & BINF_ADDED) {
+ if (!(flags & FEAT_IGNORE))
+ return 3;
+ } else
+ deletebuiltin(bnam);
+
+ return 0;
+}
+
+/*
+ * Manipulate a set of builtins. This should be called
+ * via setfeatureenables() (or, usually, via the next level up,
+ * handlefeatures()).
+ *
+ * "nam" is the name of the calling code builtin, probably "zmodload".
+ *
+ * "binl" is the builtin table containing an array of "size" builtins.
+ *
+ * "e" is either NULL, in which case all builtins in the
+ * table are removed, or else an array corresponding to "binl"
+ * with a 1 for builtins that are to be added and a 0 for builtins
+ * that are to be removed. Any builtin already in the appropriate
+ * state is left alone.
+ *
+ * Returns 1 on any error, 0 for success. The recommended way
+ * of handling errors is to compare the enables passed down
+ * with the set retrieved after the error to find what failed.
+ */
+
+/**/
+static int
+setbuiltins(char const *nam, Builtin binl, int size, int *e)
+{
+ int ret = 0, n;
+
+ for(n = 0; n < size; n++) {
+ Builtin b = &binl[n];
+ if (e && *e++) {
+ if (b->node.flags & BINF_ADDED)
+ continue;
+ if (addbuiltin(b)) {
+ zwarnnam(nam,
+ "name clash when adding builtin `%s'", b->node.nam);
+ ret = 1;
+ } else {
+ b->node.flags |= BINF_ADDED;
+ }
+ } else {
+ if (!(b->node.flags & BINF_ADDED))
+ continue;
+ if (deletebuiltin(b->node.nam)) {
+ zwarnnam(nam, "builtin `%s' already deleted", b->node.nam);
+ ret = 1;
+ } else {
+ b->node.flags &= ~BINF_ADDED;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * 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 0 on success, 1 for any failure.
+ *
+ * This should not be used from a module; instead, use handlefeatures().
+ */
+
+/**/
+mod_export int
+addbuiltins(char const *nam, Builtin binl, int size)
+{
+ int ret = 0, n;
+
+ for(n = 0; n < size; n++) {
+ Builtin b = &binl[n];
+ if(b->node.flags & BINF_ADDED)
+ continue;
+ if(addbuiltin(b)) {
+ zwarnnam(nam, "name clash when adding builtin `%s'", b->node.nam);
+ ret = 1;
+ } else {
+ b->node.flags |= BINF_ADDED;
+ }
+ }
+ return ret;
+}
+
+
+/************************************************************************
+ * Function wrappers.
+ ************************************************************************/
+
+/* 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->node.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;
+}
+
+/* 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->node.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;
+}
+
+
+/************************************************************************
+ * Conditions.
+ ************************************************************************/
+
+/* 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, const char *name, int autol)
+{
+ Conddef p;
+ int f = 1;
+ char *lookup, *s;
+
+ /* detokenize the Dash to the form encoded in lookup tables */
+ lookup = dupstring(name);
+ if (!lookup)
+ return NULL;
+ for (s = lookup; *s != '\0'; s++) {
+ if (*s == Dash)
+ *s = '-';
+ }
+
+ do {
+ for (p = condtab; p; p = p->next) {
+ if ((!!inf == !!(p->flags & CONDF_INFIX)) &&
+ !strcmp(lookup, 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) {
+ (void)ensurefeature(p->module,
+ (p->flags & CONDF_INFIX) ? "C:" : "c:",
+ (p->flags & CONDF_AUTOALL) ? NULL : lookup);
+ 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.
+ *
+ * This is used for adding both an autoload definition or
+ * a real condition. In the latter case the caller is responsible
+ * for setting the CONDF_ADDED flag.
+ */
+
+/**/
+static 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 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;
+}
+
+/*
+ * Add or remove sets of conditions. The interface is
+ * identical to setbuiltins().
+ */
+
+/**/
+static int
+setconddefs(char const *nam, Conddef c, int size, int *e)
+{
+ int ret = 0;
+
+ while (size--) {
+ if (e && *e++) {
+ if (c->flags & CONDF_ADDED) {
+ c++;
+ continue;
+ }
+ if (addconddef(c)) {
+ zwarnnam(nam, "name clash when adding condition `%s'",
+ c->name);
+ ret = 1;
+ } else {
+ c->flags |= CONDF_ADDED;
+ }
+ } else {
+ if (!(c->flags & CONDF_ADDED)) {
+ c++;
+ continue;
+ }
+ if (deleteconddef(c)) {
+ zwarnnam(nam, "condition `%s' already deleted", c->name);
+ ret = 1;
+ } else {
+ c->flags &= ~CONDF_ADDED;
+ }
+ }
+ c++;
+ }
+ return ret;
+}
+
+/* This adds a definition for autoloading a module for a condition. */
+
+/**/
+static int
+add_autocond(const char *module, const char *cnam, int flags)
+{
+ Conddef c;
+
+ c = (Conddef) zalloc(sizeof(*c));
+
+ c->name = ztrdup(cnam);
+ c->flags = ((flags & FEAT_INFIX) ? CONDF_INFIX : 0);
+ if (flags & FEAT_AUTOALL)
+ c->flags |= CONDF_AUTOALL;
+ c->module = ztrdup(module);
+
+ if (addconddef(c)) {
+ zsfree(c->name);
+ zsfree(c->module);
+ zfree(c, sizeof(*c));
+
+ if (!(flags & FEAT_IGNORE))
+ return 1;
+ }
+ return 0;
+}
+
+/* Remove a condition added with add_autocond */
+
+/**/
+static int
+del_autocond(UNUSED(const char *modnam), const char *cnam, int flags)
+{
+ Conddef cd = getconddef((flags & FEAT_INFIX) ? 1 : 0, cnam, 0);
+
+ if (!cd) {
+ if (!(flags & FEAT_IGNORE)) {
+ return 2;
+ }
+ } else if (cd->flags & CONDF_ADDED) {
+ if (!(flags & FEAT_IGNORE))
+ return 3;
+ } else
+ deleteconddef(cd);
+
+ return 0;
+}
+
+/************************************************************************
+ * Hook functions.
+ ************************************************************************/
+
+/* 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().
+ * This allows a NULL module because we call it from init.c.
+ */
+
+/**/
+mod_export int
+addhookdefs(Module m, Hookdef h, int size)
+{
+ int ret = 0;
+
+ while (size--) {
+ if (addhookdef(h)) {
+ zwarnnam(m ? m->node.nam : NULL,
+ "name clash when adding hook `%s'", h->name);
+ ret = 1;
+ }
+ h++;
+ }
+ return ret;
+}
+
+/* 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;
+}
+
+/* Remove multiple hook definitions. */
+
+/**/
+mod_export int
+deletehookdefs(UNUSED(Module m), Hookdef h, int size)
+{
+ int ret = 0;
+
+ while (size--) {
+ if (deletehookdef(h))
+ ret = 1;
+ h++;
+ }
+ return ret;
+}
+
+/* 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;
+}
+
+/* Delete a hook. */
+
+/**/
+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);
+}
+
+
+
+/************************************************************************
+ * Shell parameters.
+ ************************************************************************/
+
+/*
+ * Check that it's possible to add a parameter. This
+ * requires that either there's no parameter already present,
+ * or it's a global parameter marked for autoloading.
+ *
+ * The special status 2 is to indicate it didn't work but
+ * -i was in use so we didn't print a warning.
+ */
+
+static int
+checkaddparam(const char *nam, int opt_i)
+{
+ Param pm;
+
+ if (!(pm = (Param) gethashnode2(paramtab, nam)))
+ return 0;
+
+ if (pm->level || !(pm->node.flags & PM_AUTOLOAD)) {
+ /*
+ * -i suppresses "it's already that way" warnings,
+ * but not "this can't possibly work" warnings, so we print
+ * the message anyway if there's a local parameter blocking
+ * the parameter we want to add, not if there's a
+ * non-autoloadable parameter already there. This
+ * is consistent with the way add_auto* functions work.
+ */
+ if (!opt_i || !pm->level) {
+ zwarn("Can't add module parameter `%s': %s",
+ nam, pm->level ?
+ "local parameter exists" :
+ "parameter already exists");
+ return 1;
+ }
+ return 2;
+ }
+
+ unsetparam_pm(pm, 0, 1);
+ 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 (checkaddparam(d->name, 0))
+ return 1;
+
+ if (d->getnfn) {
+ if (!(pm = createspecialhash(d->name, d->getnfn,
+ d->scantfn, d->flags)))
+ return 1;
+ }
+ else if (!(pm = createparam(d->name, d->flags)) &&
+ !(pm = (Param) paramtab->getnode(paramtab, d->name)))
+ return 1;
+
+ d->pm = pm;
+ pm->level = 0;
+ if (d->var)
+ pm->u.data = d->var;
+ if (d->var || d->gsu) {
+ /*
+ * If no get/set/unset class, use the appropriate
+ * variable type, else use the one supplied.
+ */
+ switch (PM_TYPE(pm->node.flags)) {
+ case PM_SCALAR:
+ pm->gsu.s = d->gsu ? (GsuScalar)d->gsu : &varscalar_gsu;
+ break;
+
+ case PM_INTEGER:
+ pm->gsu.i = d->gsu ? (GsuInteger)d->gsu : &varinteger_gsu;
+ break;
+
+ case PM_FFLOAT:
+ case PM_EFLOAT:
+ pm->gsu.f = d->gsu;
+ break;
+
+ case PM_ARRAY:
+ pm->gsu.a = d->gsu ? (GsuArray)d->gsu : &vararray_gsu;
+ break;
+
+ case PM_HASHED:
+ /* hashes may behave like standard hashes */
+ if (d->gsu)
+ pm->gsu.h = (GsuHash)d->gsu;
+ break;
+
+ default:
+ unsetparam_pm(pm, 0, 1);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Delete parameters defined. No error checking yet. */
+
+/**/
+int
+deleteparamdef(Paramdef d)
+{
+ Param pm = (Param) paramtab->getnode(paramtab, d->name);
+
+ if (!pm)
+ return 1;
+ if (pm != d->pm) {
+ /*
+ * See if the parameter has been hidden. If so,
+ * bring it to the front to unset it.
+ */
+ Param prevpm, searchpm;
+ for (prevpm = pm, searchpm = pm->old;
+ searchpm;
+ prevpm = searchpm, searchpm = searchpm->old)
+ if (searchpm == d->pm)
+ break;
+
+ if (!searchpm)
+ return 1;
+
+ paramtab->removenode(paramtab, pm->node.nam);
+ prevpm->old = searchpm->old;
+ searchpm->old = pm;
+ paramtab->addnode(paramtab, searchpm->node.nam, searchpm);
+
+ pm = searchpm;
+ }
+ pm->node.flags = (pm->node.flags & ~PM_READONLY) | PM_REMOVABLE;
+ unsetparam_pm(pm, 0, 1);
+ d->pm = NULL;
+ return 0;
+}
+
+/*
+ * Add or remove sets of parameters. The interface is
+ * identical to setbuiltins().
+ */
+
+/**/
+static int
+setparamdefs(char const *nam, Paramdef d, int size, int *e)
+{
+ int ret = 0;
+
+ while (size--) {
+ if (e && *e++) {
+ if (d->pm) {
+ d++;
+ continue;
+ }
+ if (addparamdef(d)) {
+ zwarnnam(nam, "error when adding parameter `%s'", d->name);
+ ret = 1;
+ }
+ } else {
+ if (!d->pm) {
+ d++;
+ continue;
+ }
+ if (deleteparamdef(d)) {
+ zwarnnam(nam, "parameter `%s' already deleted", d->name);
+ ret = 1;
+ }
+ }
+ d++;
+ }
+ return ret;
+}
+
+/* This adds a definition for autoloading a module for a parameter. */
+
+/**/
+static int
+add_autoparam(const char *module, const char *pnam, int flags)
+{
+ Param pm;
+ int ret;
+
+ queue_signals();
+ if ((ret = checkaddparam(pnam, (flags & FEAT_IGNORE)))) {
+ unqueue_signals();
+ /*
+ * 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;
+ }
+
+ pm = setsparam(dupstring(pnam), ztrdup(module));
+
+ pm->node.flags |= PM_AUTOLOAD;
+ if (flags & FEAT_AUTOALL)
+ pm->node.flags |= PM_AUTOALL;
+ unqueue_signals();
+
+ return 0;
+}
+
+/* Remove a parameter added with add_autoparam() */
+
+/**/
+static int
+del_autoparam(UNUSED(const char *modnam), const char *pnam, int flags)
+{
+ Param pm = (Param) gethashnode2(paramtab, pnam);
+
+ if (!pm) {
+ if (!(flags & FEAT_IGNORE))
+ return 2;
+ } else if (!(pm->node.flags & PM_AUTOLOAD)) {
+ if (!(flags & FEAT_IGNORE))
+ return 3;
+ } else
+ unsetparam_pm(pm, 0, 1);
+
+ return 0;
+}
+
+/************************************************************************
+ * Math functions.
+ ************************************************************************/
+
+/* List of math functions. */
+
+/**/
+MathFunc mathfuncs;
+
+/*
+ * Remove a single math function form the list (utility function).
+ * This does not delete a module math function, that's deletemathfunc().
+ */
+
+/**/
+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));
+}
+
+/* Find a math function in the list, handling autoload if necessary. */
+
+/**/
+MathFunc
+getmathfunc(const 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 && !(p->flags & MFF_USERFUNC)) {
+ char *n = dupstring(p->module);
+ int flags = p->flags;
+
+ removemathfunc(q, p);
+
+ (void)ensurefeature(n, "f:", (flags & MFF_AUTOALL) ? NULL :
+ name);
+
+ p = getmathfunc(name, 0);
+ if (!p) {
+ zerr("autoloading module %s failed to define math function: %s", n, name);
+ }
+ }
+ return p;
+ }
+
+ return NULL;
+}
+
+/* Add a single math function */
+
+/**/
+static 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 && !(p->flags & MFF_USERFUNC)) {
+ /*
+ * Autoloadable, replace.
+ */
+ removemathfunc(q, p);
+ break;
+ }
+ return 1;
+ }
+
+ f->next = mathfuncs;
+ mathfuncs = f;
+
+ return 0;
+}
+
+/* Delete a single math function */
+
+/**/
+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;
+
+ /* the following applies to both unloaded and user-defined functions */
+ if (f->module) {
+ zsfree(f->name);
+ zsfree(f->module);
+ zfree(f, sizeof(*f));
+ } else
+ f->flags &= ~MFF_ADDED;
+
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * Add or remove sets of math functions. The interface is
+ * identical to setbuiltins().
+ */
+
+/**/
+static int
+setmathfuncs(char const *nam, MathFunc f, int size, int *e)
+{
+ int ret = 0;
+
+ while (size--) {
+ if (e && *e++) {
+ if (f->flags & MFF_ADDED) {
+ f++;
+ continue;
+ }
+ if (addmathfunc(f)) {
+ zwarnnam(nam, "name clash when adding math function `%s'",
+ f->name);
+ ret = 1;
+ } else {
+ f->flags |= MFF_ADDED;
+ }
+ } else {
+ if (!(f->flags & MFF_ADDED)) {
+ f++;
+ continue;
+ }
+ if (deletemathfunc(f)) {
+ zwarnnam(nam, "math function `%s' already deleted", f->name);
+ ret = 1;
+ } else {
+ f->flags &= ~MFF_ADDED;
+ }
+ }
+ f++;
+ }
+ return ret;
+}
+
+/* Add an autoload definition for a math function. */
+
+/**/
+static int
+add_automathfunc(const char *module, const char *fnam, int flags)
+{
+ MathFunc f;
+
+ f = (MathFunc) zalloc(sizeof(*f));
+
+ f->name = ztrdup(fnam);
+ f->module = ztrdup(module);
+ f->flags = 0;
+
+ if (addmathfunc(f)) {
+ zsfree(f->name);
+ zsfree(f->module);
+ zfree(f, sizeof(*f));
+
+ if (!(flags & FEAT_IGNORE))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Remove a math function added with add_automathfunc() */
+
+/**/
+static int
+del_automathfunc(UNUSED(const char *modnam), const char *fnam, int flags)
+{
+ MathFunc f = getmathfunc(fnam, 0);
+
+ if (!f) {
+ if (!(flags & FEAT_IGNORE))
+ return 2;
+ } else if (f->flags & MFF_ADDED) {
+ if (!(flags & FEAT_IGNORE))
+ return 3;
+ } else
+ deletemathfunc(f);
+
+ return 0;
+}
+
+/************************************************************************
+ * Now support for dynamical loading and the fallback functions
+ * we use for loading if dynamical loading is not available.
+ ************************************************************************/
+
+/**/
+#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) {
+ Module m;
+ int i, err = loadbind(0, (void *) addbuiltin, ret);
+ for (i = 0; i < modulestab->hsize && !err; i++) {
+ for (m = (Module)modulestab->nodes[i]; m && !err;
+ m = (Module)m->node.next) {
+ if (!(m->node.flags & MOD_ALIAS) &&
+ m->u.handle && !(m->node.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])
+#ifndef HAVE_DLERROR
+# define HAVE_DLERROR 1
+#endif
+
+/**/
+#else
+
+#ifdef HAVE_DLFCN_H
+# if defined(HAVE_DL_H) && defined(HPUX10DYNAMIC)
+# 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 HPUX10DYNAMIC
+# 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)
+# ifdef HAVE_DLERROR /* paranoia */
+# undef HAVE_DLERROR
+# endif
+#else
+# ifndef HAVE_DLCLOSE
+# define dlclose(X) ((X), 0)
+# endif
+/**/
+#endif
+
+#ifdef DLSYM_NEEDS_UNDERSCORE
+# define STR_SETUP "_setup_"
+# define STR_FEATURES "_features_"
+# define STR_ENABLES "_enables_"
+# define STR_BOOT "_boot_"
+# define STR_CLEANUP "_cleanup_"
+# define STR_FINISH "_finish_"
+#else /* !DLSYM_NEEDS_UNDERSCORE */
+# define STR_SETUP "setup_"
+# define STR_FEATURES "features_"
+# define STR_ENABLES "enables_"
+# 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
+
+/*
+ * Attempt to load a module. This is the lowest level of
+ * zsh function for dynamical modules. Returns the handle
+ * from the dynamic loader.
+ */
+
+/**/
+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);
+ unmetafy(buf, NULL);
+ if (*buf) /* dlopen(NULL) returns a handle to the main binary */
+ ret = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
+ }
+
+ return ret;
+}
+
+/*
+ * Load a module, with option to complain or not.
+ * Returns the handle from the dynamic loader.
+ */
+
+/**/
+static void *
+do_load_module(char const *name, int silent)
+{
+ void *ret;
+
+ ret = try_load_module(name);
+ if (!ret && !silent) {
+#ifdef HAVE_DLERROR
+ char *errstr = dlerror();
+ zwarn("failed to load module `%s': %s", name,
+ errstr ? metafy(errstr, -1, META_HEAPDUP) : "empty module path");
+#else
+ zwarn("failed to load module: %s", name);
+#endif
+ }
+ return ret;
+}
+
+/**/
+#else /* !DYNAMIC */
+
+/*
+ * Dummy loader when no dynamic loading available; always fails.
+ */
+
+/**/
+static void *
+do_load_module(char const *name, int silent)
+{
+ if (!silent)
+ zwarn("failed to load module: %s", name);
+
+ return NULL;
+}
+
+/**/
+#endif /* !DYNAMIC */
+
+/*
+ * Find a module in the list.
+ * 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.
+ * 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.
+ */
+/**/
+static Module
+find_module(const char *name, int flags, const char **namep)
+{
+ Module m;
+
+ m = (Module)modulestab->getnode2(modulestab, name);
+ if (m) {
+ if ((flags & FINDMOD_ALIASP) && (m->node.flags & MOD_ALIAS)) {
+ if (namep)
+ *namep = m->u.alias;
+ return find_module(m->u.alias, flags, namep);
+ }
+ if (namep)
+ *namep = m->node.nam;
+ return m;
+ }
+ if (!(flags & FINDMOD_CREATE))
+ return NULL;
+ m = zshcalloc(sizeof(*m));
+ modulestab->addnode(modulestab, ztrdup(name), m);
+ return m;
+}
+
+/*
+ * Unlink and free a module node from the linked list.
+ */
+
+/**/
+static void
+delete_module(Module m)
+{
+ modulestab->removenode(modulestab, m->node.nam);
+
+ modulestab->freenode(&m->node);
+}
+
+/*
+ * Return 1 if a module is fully loaded else zero.
+ * A linked module may be marked as unloaded even though
+ * we can't fully unload it; this returns 0 to try to
+ * make that state transparently like an unloaded module.
+ */
+
+/**/
+mod_export int
+module_loaded(const char *name)
+{
+ Module m;
+
+ return ((m = find_module(name, FINDMOD_ALIASP, NULL)) &&
+ m->u.handle &&
+ !(m->node.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, void*))) m->u.handle)(0, m, NULL);
+}
+
+/**/
+static int
+dyn_features_module(Module m, char ***features)
+{
+ return ((int (*)_((int,Module, void*))) m->u.handle)(4, m, features);
+}
+
+/**/
+static int
+dyn_enables_module(Module m, int **enables)
+{
+ return ((int (*)_((int,Module, void*))) m->u.handle)(5, m, enables);
+}
+
+/**/
+static int
+dyn_boot_module(Module m)
+{
+ return ((int (*)_((int,Module, void*))) m->u.handle)(1, m, NULL);
+}
+
+/**/
+static int
+dyn_cleanup_module(Module m)
+{
+ return ((int (*)_((int,Module, void*))) m->u.handle)(2, m, NULL);
+}
+
+/**/
+static int
+dyn_finish_module(Module m)
+{
+ return ((int (*)_((int,Module,void *))) m->u.handle)(3, m, NULL);
+}
+
+/**/
+#else
+
+static Module_generic_func
+module_func(Module m, char *name)
+{
+#ifdef DYNAMIC_NAME_CLASH_OK
+ return (Module_generic_func) dlsym(m->u.handle, name);
+#else /* !DYNAMIC_NAME_CLASH_OK */
+ VARARR(char, buf, strlen(name) + strlen(m->node.nam)*2 + 1);
+ char const *p;
+ char *q;
+ strcpy(buf, name);
+ q = strchr(buf, 0);
+ for(p = m->node.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_generic_func) dlsym(m->u.handle, buf);
+#endif /* !DYNAMIC_NAME_CLASH_OK */
+}
+
+/**/
+static int
+dyn_setup_module(Module m)
+{
+ Module_void_func fn = (Module_void_func)module_func(m, STR_SETUP);
+
+ if (fn)
+ return fn(m);
+ zwarnnam(m->node.nam, "no setup function");
+ return 1;
+}
+
+/**/
+static int
+dyn_features_module(Module m, char ***features)
+{
+ Module_features_func fn =
+ (Module_features_func)module_func(m, STR_FEATURES);
+
+ if (fn)
+ return fn(m, features);
+ /* not a user-visible error if no features function */
+ return 1;
+}
+
+/**/
+static int
+dyn_enables_module(Module m, int **enables)
+{
+ Module_enables_func fn = (Module_enables_func)module_func(m, STR_ENABLES);
+
+ if (fn)
+ return fn(m, enables);
+ /* not a user-visible error if no enables function */
+ return 1;
+}
+
+/**/
+static int
+dyn_boot_module(Module m)
+{
+ Module_void_func fn = (Module_void_func)module_func(m, STR_BOOT);
+
+ if(fn)
+ return fn(m);
+ zwarnnam(m->node.nam, "no boot function");
+ return 1;
+}
+
+/**/
+static int
+dyn_cleanup_module(Module m)
+{
+ Module_void_func fn = (Module_void_func)module_func(m, STR_CLEANUP);
+
+ if(fn)
+ return fn(m);
+ zwarnnam(m->node.nam, "no cleanup function");
+ 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_void_func fn = (Module_void_func)module_func(m, STR_FINISH);
+ int r;
+
+ if (fn)
+ r = fn(m);
+ else {
+ zwarnnam(m->node.nam, "no finish function");
+ r = 1;
+ }
+ dlclose(m->u.handle);
+ return r;
+}
+
+/**/
+#endif /* !AIXDYNAMIC */
+
+/**/
+static int
+setup_module(Module m)
+{
+ return ((m->node.flags & MOD_LINKED) ?
+ (m->u.linked->setup)(m) : dyn_setup_module(m));
+}
+
+/**/
+static int
+features_module(Module m, char ***features)
+{
+ return ((m->node.flags & MOD_LINKED) ?
+ (m->u.linked->features)(m, features) :
+ dyn_features_module(m, features));
+}
+
+/**/
+static int
+enables_module(Module m, int **enables)
+{
+ return ((m->node.flags & MOD_LINKED) ?
+ (m->u.linked->enables)(m, enables) :
+ dyn_enables_module(m, enables));
+}
+
+/**/
+static int
+boot_module(Module m)
+{
+ return ((m->node.flags & MOD_LINKED) ?
+ (m->u.linked->boot)(m) : dyn_boot_module(m));
+}
+
+/**/
+static int
+cleanup_module(Module m)
+{
+ return ((m->node.flags & MOD_LINKED) ?
+ (m->u.linked->cleanup)(m) : dyn_cleanup_module(m));
+}
+
+/**/
+static int
+finish_module(Module m)
+{
+ return ((m->node.flags & MOD_LINKED) ?
+ (m->u.linked->finish)(m) : dyn_finish_module(m));
+}
+
+/**/
+#else /* !DYNAMIC */
+
+/**/
+static int
+setup_module(Module m)
+{
+ return ((m->node.flags & MOD_LINKED) ? (m->u.linked->setup)(m) : 1);
+}
+
+/**/
+static int
+features_module(Module m, char ***features)
+{
+ return ((m->node.flags & MOD_LINKED) ? (m->u.linked->features)(m, features)
+ : 1);
+}
+
+/**/
+static int
+enables_module(Module m, int **enables)
+{
+ return ((m->node.flags & MOD_LINKED) ? (m->u.linked->enables)(m, enables)
+ : 1);
+}
+
+/**/
+static int
+boot_module(Module m)
+{
+ return ((m->node.flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1);
+}
+
+/**/
+static int
+cleanup_module(Module m)
+{
+ return ((m->node.flags & MOD_LINKED) ? (m->u.linked->cleanup)(m) : 1);
+}
+
+/**/
+static int
+finish_module(Module m)
+{
+ return ((m->node.flags & MOD_LINKED) ? (m->u.linked->finish)(m) : 1);
+}
+
+/**/
+#endif /* !DYNAMIC */
+
+
+/************************************************************************
+ * Functions called when manipulating modules
+ ************************************************************************/
+
+/*
+ * Set the features for the module, which must be loaded
+ * by now (though may not be fully set up).
+ *
+ * Return 0 for success, 1 for failure, 2 if some features
+ * couldn't be set by the module itself (non-existent features
+ * are tested here and cause 1 to be returned).
+ */
+
+/**/
+static int
+do_module_features(Module m, Feature_enables enablesarr, int flags)
+{
+ char **features;
+ int ret = 0;
+
+ if (features_module(m, &features) == 0) {
+ /*
+ * Features are supported. If we were passed
+ * a NULL array, enable all features, else
+ * enable only the features listed.
+ * (This may in principle be an empty array,
+ * although that's not very pointful.)
+ */
+ int *enables = NULL;
+ if (enables_module(m, &enables)) {
+ /* If features are supported, enables should be, too */
+ if (!(flags & FEAT_IGNORE))
+ zwarn("error getting enabled features for module `%s'",
+ m->node.nam);
+ return 1;
+ }
+
+ if ((flags & FEAT_CHECKAUTO) && m->autoloads) {
+ /*
+ * Check autoloads are available. Since these
+ * have been requested at some other point, they
+ * don't affect the return status unless something
+ * in enablesstr doesn't work.
+ */
+ LinkNode an, nextn;
+ for (an = firstnode(m->autoloads); an; an = nextn) {
+ char *al = (char *)getdata(an), **ptr;
+ /* careful, we can delete the current node */
+ nextn = nextnode(an);
+ for (ptr = features; *ptr; ptr++)
+ if (!strcmp(al, *ptr))
+ break;
+ if (!*ptr) {
+ char *arg[2];
+ if (!(flags & FEAT_IGNORE))
+ zwarn(
+ "module `%s' has no such feature: `%s': autoload cancelled",
+ m->node.nam, al);
+ /*
+ * This shouldn't happen, so it's not worth optimising
+ * the call to autofeatures...
+ */
+ arg[0] = al = dupstring(al);
+ arg[1] = NULL;
+ (void)autofeatures(NULL, m->node.nam, arg, 0,
+ FEAT_IGNORE|FEAT_REMOVE);
+ /*
+ * don't want to try to enable *that*...
+ * expunge it from the enable string.
+ */
+ if (enablesarr) {
+ Feature_enables fep;
+ for (fep = enablesarr; fep->str; fep++) {
+ char *str = fep->str;
+ if (*str == '+' || *str == '-')
+ str++;
+ if (fep->pat ? pattry(fep->pat, al) :
+ !strcmp(al, str)) {
+ /* can't enable it after all, so return 1 */
+ ret = 1;
+ while (fep->str) {
+ fep->str = fep[1].str;
+ fep->pat = fep[1].pat;
+ fep++;
+ }
+ if (!fep->pat)
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (enablesarr) {
+ Feature_enables fep;
+ for (fep = enablesarr; fep->str; fep++) {
+ char **fp, *esp = fep->str;
+ int on = 1, found = 0;
+ if (*esp == '+')
+ esp++;
+ else if (*esp == '-') {
+ on = 0;
+ esp++;
+ }
+ for (fp = features; *fp; fp++)
+ if (fep->pat ? pattry(fep->pat, *fp) : !strcmp(*fp, esp)) {
+ enables[fp - features] = on;
+ found++;
+ if (!fep->pat)
+ break;
+ }
+ if (!found) {
+ if (!(flags & FEAT_IGNORE))
+ zwarn(fep->pat ?
+ "module `%s' has no feature matching: `%s'" :
+ "module `%s' has no such feature: `%s'",
+ m->node.nam, esp);
+ return 1;
+ }
+ }
+ } else {
+ /*
+ * Enable all features. This is used when loading
+ * without using zmodload -F.
+ */
+ int n_features = arrlen(features);
+ int *ep;
+ for (ep = enables; n_features--; ep++)
+ *ep = 1;
+ }
+
+ if (enables_module(m, &enables))
+ return 2;
+ } else if (enablesarr) {
+ if (!(flags & FEAT_IGNORE))
+ zwarn("module `%s' does not support features", m->node.nam);
+ return 1;
+ }
+ /* Else it doesn't support features but we don't care. */
+
+ return ret;
+}
+
+/*
+ * Boot the module, including setting up features.
+ * As we've only just loaded the module, we don't yet
+ * know what features it supports, so we get them passed
+ * as a string.
+ *
+ * Returns 0 if OK, 1 if completely failed, 2 if some features
+ * couldn't be set up.
+ */
+
+/**/
+static int
+do_boot_module(Module m, Feature_enables enablesarr, int silent)
+{
+ int ret = do_module_features(m, enablesarr,
+ silent ? FEAT_IGNORE|FEAT_CHECKAUTO :
+ FEAT_CHECKAUTO);
+
+ if (ret == 1)
+ return 1;
+
+ if (boot_module(m))
+ return 1;
+ return ret;
+}
+
+/*
+ * Cleanup the module.
+ */
+
+/**/
+static int
+do_cleanup_module(Module m)
+{
+ return (m->node.flags & MOD_LINKED) ?
+ (m->u.linked && m->u.linked->cleanup(m)) :
+ (m->u.handle && cleanup_module(m));
+}
+
+/*
+ * Test a module name contains only valid characters: those
+ * allowed in a shell identifier plus slash. Return 1 if so.
+ */
+
+/**/
+static int
+modname_ok(char const *p)
+{
+ do {
+ p = itype_end(p, IIDENT, 0);
+ if (!*p)
+ return 1;
+ } while(*p++ == '/');
+ return 0;
+}
+
+/*
+ * High level function to load a module, encapsulating
+ * all the handling of module functions.
+ *
+ * "*enablesstr" is NULL if the caller is not feature-aware;
+ * then the module should turn on all features. If it
+ * is not NULL it points to an array of features to be
+ * turned on. This function is responsible for testing whether
+ * the module supports those features.
+ *
+ * If "silent" is 1, don't issue warnings for errors.
+ *
+ * Now returns 0 for success (changed post-4.3.4),
+ * 1 for complete failure, 2 if some features couldn't be set.
+ */
+
+/**/
+mod_export int
+load_module(char const *name, Feature_enables enablesarr, int silent)
+{
+ Module m;
+ void *handle = NULL;
+ Linkedmod linked;
+ int set, bootret;
+
+ if (!modname_ok(name)) {
+ if (!silent)
+ zerr("invalid module name `%s'", name);
+ return 1;
+ }
+ /*
+ * 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 (!(m = find_module(name, FINDMOD_ALIASP, &name))) {
+ if (!(linked = module_linked(name)) &&
+ !(handle = do_load_module(name, silent))) {
+ unqueue_signals();
+ return 1;
+ }
+ m = zshcalloc(sizeof(*m));
+ if (handle) {
+ m->u.handle = handle;
+ m->node.flags |= MOD_SETUP;
+ } else {
+ m->u.linked = linked;
+ m->node.flags |= MOD_SETUP | MOD_LINKED;
+ }
+ modulestab->addnode(modulestab, ztrdup(name), m);
+
+ if ((set = setup_module(m)) ||
+ (bootret = do_boot_module(m, enablesarr, silent)) == 1) {
+ if (!set)
+ do_cleanup_module(m);
+ finish_module(m);
+ delete_module(m);
+ unqueue_signals();
+ return 1;
+ }
+ m->node.flags |= MOD_INIT_S | MOD_INIT_B;
+ m->node.flags &= ~MOD_SETUP;
+ unqueue_signals();
+ return bootret;
+ }
+ if (m->node.flags & MOD_SETUP) {
+ unqueue_signals();
+ return 0;
+ }
+ if (m->node.flags & MOD_UNLOAD)
+ m->node.flags &= ~MOD_UNLOAD;
+ else if ((m->node.flags & MOD_LINKED) ? m->u.linked : m->u.handle) {
+ unqueue_signals();
+ return 0;
+ }
+ if (m->node.flags & MOD_BUSY) {
+ unqueue_signals();
+ zerr("circular dependencies for module ;%s", name);
+ return 1;
+ }
+ m->node.flags |= MOD_BUSY;
+ /*
+ * TODO: shouldn't we unload the module if one of
+ * its dependencies fails?
+ */
+ if (m->deps) {
+ LinkNode n;
+ for (n = firstnode(m->deps); n; incnode(n))
+ if (load_module((char *) getdata(n), NULL, silent) == 1) {
+ m->node.flags &= ~MOD_BUSY;
+ unqueue_signals();
+ return 1;
+ }
+ }
+ m->node.flags &= ~MOD_BUSY;
+ if (!m->u.handle) {
+ handle = NULL;
+ if (!(linked = module_linked(name)) &&
+ !(handle = do_load_module(name, silent))) {
+ unqueue_signals();
+ return 1;
+ }
+ if (handle) {
+ m->u.handle = handle;
+ m->node.flags |= MOD_SETUP;
+ } else {
+ m->u.linked = linked;
+ m->node.flags |= MOD_SETUP | MOD_LINKED;
+ }
+ if (setup_module(m)) {
+ finish_module(m);
+ if (handle)
+ m->u.handle = NULL;
+ else
+ m->u.linked = NULL;
+ m->node.flags &= ~MOD_SETUP;
+ unqueue_signals();
+ return 1;
+ }
+ m->node.flags |= MOD_INIT_S;
+ }
+ m->node.flags |= MOD_SETUP;
+ if ((bootret = do_boot_module(m, enablesarr, silent)) == 1) {
+ do_cleanup_module(m);
+ finish_module(m);
+ if (m->node.flags & MOD_LINKED)
+ m->u.linked = NULL;
+ else
+ m->u.handle = NULL;
+ m->node.flags &= ~MOD_SETUP;
+ unqueue_signals();
+ return 1;
+ }
+ m->node.flags |= MOD_INIT_B;
+ m->node.flags &= ~MOD_SETUP;
+ unqueue_signals();
+ return bootret;
+}
+
+/* This ensures that the module with the name given as the first argument
+ * is loaded.
+ * The other argument is the array of features to set. If this is NULL
+ * all features are enabled (even if the module was already loaded).
+ *
+ * If this is non-NULL the module features are set accordingly
+ * whether or not the module is loaded; it is an error if the
+ * module does not support the features passed (even if the feature
+ * is to be turned off) or if the module does not support features
+ * at all.
+ * The return value is 0 if the module was found or loaded
+ * (this changed post-4.3.4, because I got so confused---pws),
+ * 1 if loading failed completely, 2 if some features couldn't be set.
+ *
+ * This function behaves like load_module() except that it
+ * handles the case where the module was already loaded, and
+ * sets features accordingly.
+ */
+
+/**/
+mod_export int
+require_module(const char *module, Feature_enables features, int silent)
+{
+ Module m = NULL;
+ int ret = 0;
+
+ /* Resolve aliases and actual loadable module as for load_module */
+ queue_signals();
+ m = find_module(module, FINDMOD_ALIASP, &module);
+ if (!m || !m->u.handle ||
+ (m->node.flags & MOD_UNLOAD))
+ ret = load_module(module, features, silent);
+ else
+ ret = do_module_features(m, features, 0);
+ unqueue_signals();
+
+ return ret;
+}
+
+/*
+ * Indicate that the module named "name" depends on the module
+ * named "from".
+ */
+
+/**/
+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.)
+ */
+ m = find_module(name, FINDMOD_ALIASP|FINDMOD_CREATE, &name);
+ 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));
+}
+
+/*
+ * Function to be used when scanning the builtins table to
+ * find and print autoloadable builtins.
+ */
+
+/**/
+static void
+autoloadscan(HashNode hn, int printflags)
+{
+ Builtin bn = (Builtin) hn;
+
+ if(bn->node.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->node.nam, bn->optstr)) {
+ putchar(' ');
+ quotedzputs(bn->node.nam, stdout);
+ }
+ } else {
+ nicezputs(bn->node.nam, stdout);
+ if(strcmp(bn->node.nam, bn->optstr)) {
+ fputs(" (", stdout);
+ nicezputs(bn->optstr, stdout);
+ putchar(')');
+ }
+ }
+ putchar('\n');
+}
+
+
+/************************************************************************
+ * Handling for the zmodload builtin and its various options.
+ ************************************************************************/
+
+/*
+ * Main builtin entry point for zmodload.
+ */
+
+/**/
+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, autoopts;
+ /* options only allowed with -F */
+ char *fonly = "lP", *fp;
+
+ if (ops_bcpf && !ops_au) {
+ zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u");
+ return 1;
+ }
+ if (OPT_ISSET(ops,'F') && (ops_bcpf || OPT_ISSET(ops,'u'))) {
+ zwarnnam(nam, "-b, -c, -f, -p and -u cannot be combined with -F");
+ 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");
+ 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");
+ return 1;
+ }
+ if (OPT_ISSET(ops,'u') && !*args) {
+ zwarnnam(nam, "what do you want to unload?");
+ return 1;
+ }
+ if (OPT_ISSET(ops,'e') && (OPT_ISSET(ops,'I') || OPT_ISSET(ops,'L') ||
+ (OPT_ISSET(ops,'a') && !OPT_ISSET(ops,'F'))
+ || OPT_ISSET(ops,'d') ||
+ OPT_ISSET(ops,'i') || OPT_ISSET(ops,'u'))) {
+ zwarnnam(nam, "-e cannot be combined with other options");
+ /* except -F ... */
+ return 1;
+ }
+ for (fp = fonly; *fp; fp++) {
+ if (OPT_ISSET(ops,STOUC(*fp)) && !OPT_ISSET(ops,'F')) {
+ zwarnnam(nam, "-%c is only allowed with -F", *fp);
+ return 1;
+ }
+ }
+ queue_signals();
+ if (OPT_ISSET(ops, 'F'))
+ ret = bin_zmodload_features(nam, args, ops);
+ else 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 ((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);
+ unqueue_signals();
+
+ return ret;
+}
+
+/* zmodload -A */
+
+/**/
+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.
+ */
+ Module m;
+
+ if (!*args) {
+ if (OPT_ISSET(ops,'R')) {
+ zwarnnam(nam, "no module alias to remove");
+ return 1;
+ }
+ scanhashtable(modulestab, 1, MOD_ALIAS, 0,
+ modulestab->printnode,
+ OPT_ISSET(ops,'L') ? PRINTMOD_LIST : 0);
+ return 0;
+ }
+
+ for (; *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);
+ return 1;
+ }
+ if (OPT_ISSET(ops,'R')) {
+ if (aliasname) {
+ zwarnnam(nam, "bad syntax for removing module alias: %s",
+ *args);
+ return 1;
+ }
+ m = find_module(*args, 0, NULL);
+ if (m) {
+ if (!(m->node.flags & MOD_ALIAS)) {
+ zwarnnam(nam, "module is not an alias: %s", *args);
+ return 1;
+ }
+ delete_module(m);
+ } else {
+ zwarnnam(nam, "no such module alias: %s", *args);
+ return 1;
+ }
+ } else {
+ if (aliasname) {
+ const char *mname = aliasname;
+ if (!modname_ok(aliasname)) {
+ zwarnnam(nam, "invalid module name `%s'", aliasname);
+ return 1;
+ }
+ do {
+ if (!strcmp(mname, *args)) {
+ zwarnnam(nam, "module alias would refer to itself: %s",
+ *args);
+ return 1;
+ }
+ } while ((m = find_module(mname, 0, NULL))
+ && (m->node.flags & MOD_ALIAS)
+ && (mname = m->u.alias));
+ m = find_module(*args, 0, NULL);
+ if (m) {
+ if (!(m->node.flags & MOD_ALIAS)) {
+ zwarnnam(nam, "module is not an alias: %s", *args);
+ return 1;
+ }
+ zsfree(m->u.alias);
+ } else {
+ m = (Module) zshcalloc(sizeof(*m));
+ m->node.flags = MOD_ALIAS;
+ modulestab->addnode(modulestab, ztrdup(*args), m);
+ }
+ m->u.alias = ztrdup(aliasname);
+ } else {
+ if ((m = find_module(*args, 0, NULL))) {
+ if (m->node.flags & MOD_ALIAS)
+ modulestab->printnode(&m->node,
+ OPT_ISSET(ops,'L') ?
+ PRINTMOD_LIST : 0);
+ else {
+ zwarnnam(nam, "module is not an alias: %s", *args);
+ return 1;
+ }
+ } else {
+ zwarnnam(nam, "no such module alias: %s", *args);
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* zmodload -e (without -F) */
+
+/**/
+static int
+bin_zmodload_exist(UNUSED(char *nam), char **args, Options ops)
+{
+ Module m;
+
+ if (!*args) {
+ scanhashtable(modulestab, 1, 0, 0, modulestab->printnode,
+ OPT_ISSET(ops,'A') ? PRINTMOD_EXIST|PRINTMOD_ALIAS :
+ PRINTMOD_EXIST);
+ return 0;
+ } else {
+ int ret = 0;
+
+ for (; !ret && *args; args++) {
+ if (!(m = find_module(*args, FINDMOD_ALIASP, NULL))
+ || !m->u.handle
+ || (m->node.flags & MOD_UNLOAD))
+ ret = 1;
+ }
+ return ret;
+ }
+}
+
+/* zmodload -d */
+
+/**/
+static int
+bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops)
+{
+ Module m;
+ if (OPT_ISSET(ops,'u')) {
+ /* remove dependencies, which can't pertain to aliases */
+ const char *tnam = *args++;
+ m = find_module(tnam, FINDMOD_ALIASP, &tnam);
+ if (!m)
+ return 0;
+ 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(m);
+ return 0;
+ } else if (!args[0] || !args[1]) {
+ /* list dependencies */
+ int depflags = OPT_ISSET(ops,'L') ?
+ PRINTMOD_DEPS|PRINTMOD_LIST : PRINTMOD_DEPS;
+ if (args[0]) {
+ if ((m = (Module)modulestab->getnode2(modulestab, args[0])))
+ modulestab->printnode(&m->node, depflags);
+ } else {
+ scanhashtable(modulestab, 1, 0, 0, modulestab->printnode,
+ depflags);
+ }
+ return 0;
+ } else {
+ /* add dependencies */
+ int ret = 0;
+ char *tnam = *args++;
+
+ for (; *args; args++)
+ add_dep(tnam, *args);
+ return ret;
+ }
+}
+
+/*
+ * Function for scanning the parameter table to find and print
+ * out autoloadable parameters.
+ */
+
+static void
+printautoparams(HashNode hn, int lon)
+{
+ Param pm = (Param) hn;
+
+ if (pm->node.flags & PM_AUTOLOAD) {
+ if (lon)
+ printf("zmodload -ap %s %s\n", pm->u.str, pm->node.nam);
+ else
+ printf("%s (%s)\n", pm->node.nam, pm->u.str);
+ }
+}
+
+/* zmodload -a/u [bcpf] */
+
+/**/
+static int
+bin_zmodload_auto(char *nam, char **args, Options ops)
+{
+ int fchar, flags;
+ 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';
+ }
+
+ flags = FEAT_AUTOALL;
+ if (OPT_ISSET(ops,'i'))
+ flags |= FEAT_IGNORE;
+ if (OPT_ISSET(ops,'u')) {
+ /* remove autoload */
+ flags |= FEAT_REMOVE;
+ modnam = NULL;
+ } else {
+ /* add autoload */
+ modnam = *args;
+
+ if (args[1])
+ args++;
+ }
+ return autofeatures(nam, modnam, args, fchar, flags);
+}
+
+/* Backend handler for zmodload -u */
+
+/**/
+int
+unload_module(Module m)
+{
+ int del;
+
+ /*
+ * Only unload the real module, so resolve aliases.
+ */
+ if (m->node.flags & MOD_ALIAS) {
+ m = find_module(m->u.alias, FINDMOD_ALIASP, NULL);
+ if (!m)
+ return 1;
+ }
+ /*
+ * We may need to clean up the module any time setup_ has been
+ * called. After cleanup_ is successful we are no longer in the
+ * booted state (because features etc. are deregistered), so remove
+ * MOD_INIT_B, and also MOD_INIT_S since we won't need to cleanup
+ * again if this succeeded.
+ */
+ if ((m->node.flags & MOD_INIT_S) &&
+ !(m->node.flags & MOD_UNLOAD) &&
+ do_cleanup_module(m))
+ return 1;
+ m->node.flags &= ~(MOD_INIT_B|MOD_INIT_S);
+
+ del = (m->node.flags & MOD_UNLOAD);
+
+ if (m->wrapper) {
+ m->node.flags |= MOD_UNLOAD;
+ return 0;
+ }
+ m->node.flags &= ~MOD_UNLOAD;
+
+ /*
+ * We always need to finish the module (and unload it)
+ * if it is present.
+ */
+ if (m->node.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)) {
+ Module dm = find_module((char *) getdata(n),
+ FINDMOD_ALIASP, NULL);
+
+ if (dm &&
+ (dm->node.flags & MOD_UNLOAD)) {
+ /* See if this is the only module depending on it. */
+ Module am;
+ int du = 1, i;
+ /* Scan hash table the hard way */
+ for (i = 0; du && i < modulestab->hsize; i++) {
+ for (am = (Module)modulestab->nodes[i]; du && am;
+ am = (Module)am->node.next) {
+ LinkNode sn;
+ /*
+ * Don't scan the module we're unloading;
+ * ignore if no dependencies.
+ */
+ if (am == m || !am->deps)
+ continue;
+ /* Don't scan if not loaded nor linked */
+ if ((am->node.flags & MOD_LINKED) ?
+ !am->u.linked : !am->u.handle)
+ continue;
+ for (sn = firstnode(am->deps); du && sn;
+ incnode(sn)) {
+ if (!strcmp((char *) getdata(sn),
+ dm->node.nam))
+ du = 0;
+ }
+ }
+ }
+ if (du)
+ unload_module(dm);
+ }
+ }
+ }
+ if (m->autoloads && firstnode(m->autoloads)) {
+ /*
+ * Module has autoloadable features. Restore them
+ * so that the module will be reloaded when needed.
+ */
+ autofeatures("zsh", m->node.nam,
+ hlinklist2array(m->autoloads, 0), 0, FEAT_IGNORE);
+ } else if (!m->deps) {
+ delete_module(m);
+ }
+ return 0;
+}
+
+/*
+ * Unload a module by name (modname); nam is the command name.
+ * Optionally don't print some error messages (always print
+ * dependency errors).
+ */
+
+/**/
+int
+unload_named_module(char *modname, char *nam, int silent)
+{
+ const char *mname;
+ Module m;
+ int ret = 0;
+
+ m = find_module(modname, FINDMOD_ALIASP, &mname);
+ if (m) {
+ int i, del = 0;
+ Module dm;
+
+ for (i = 0; i < modulestab->hsize; i++) {
+ for (dm = (Module)modulestab->nodes[i]; dm;
+ dm = (Module)dm->node.next) {
+ LinkNode dn;
+ if (!dm->deps || !dm->u.handle)
+ continue;
+ for (dn = firstnode(dm->deps); dn; incnode(dn)) {
+ if (!strcmp((char *) getdata(dn), mname)) {
+ if (dm->node.flags & MOD_UNLOAD)
+ del = 1;
+ else {
+ zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", mname);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ if (del)
+ m->wrapper++;
+ if (unload_module(m))
+ ret = 1;
+ if (del)
+ m->wrapper--;
+ } else if (!silent) {
+ zwarnnam(nam, "no such module %s", modname);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/* zmodload -u without -d */
+
+/**/
+static int
+bin_zmodload_load(char *nam, char **args, Options ops)
+{
+ int ret = 0;
+ if(OPT_ISSET(ops,'u')) {
+ /* unload modules */
+ for(; *args; args++) {
+ if (unload_named_module(*args, nam, OPT_ISSET(ops,'i')))
+ ret = 1;
+ }
+ return ret;
+ } else if(!*args) {
+ /* list modules */
+ scanhashtable(modulestab, 1, 0, MOD_UNLOAD|MOD_ALIAS,
+ modulestab->printnode,
+ OPT_ISSET(ops,'L') ? PRINTMOD_LIST : 0);
+ return 0;
+ } else {
+ /* load modules */
+ for (; *args; args++) {
+ int tmpret = require_module(*args, NULL, OPT_ISSET(ops,'s'));
+ if (tmpret && ret != 1)
+ ret = tmpret;
+ }
+
+ return ret;
+ }
+}
+
+/* zmodload -F */
+
+/**/
+static int
+bin_zmodload_features(const char *nam, char **args, Options ops)
+{
+ int iarg;
+ char *modname = *args;
+ Patprog *patprogs;
+ Feature_enables features, fep;
+
+ if (modname)
+ args++;
+ else if (OPT_ISSET(ops,'L')) {
+ int printflags = PRINTMOD_LIST|PRINTMOD_FEATURES;
+ if (OPT_ISSET(ops,'P')) {
+ zwarnnam(nam, "-P is only allowed with a module name");
+ return 1;
+ }
+ if (OPT_ISSET(ops,'l'))
+ printflags |= PRINTMOD_LISTALL;
+ if (OPT_ISSET(ops,'a'))
+ printflags |= PRINTMOD_AUTO;
+ scanhashtable(modulestab, 1, 0, MOD_ALIAS,
+ modulestab->printnode, printflags);
+ return 0;
+ }
+
+ if (!modname) {
+ zwarnnam(nam, "-F requires a module name");
+ return 1;
+ }
+
+ if (OPT_ISSET(ops,'m')) {
+ char **argp;
+ Patprog *patprogp;
+
+ /* not NULL terminated */
+ patprogp = patprogs =
+ (Patprog *)zhalloc(arrlen(args)*sizeof(Patprog));
+ for (argp = args; *argp; argp++, patprogp++) {
+ char *arg = *argp;
+ if (*arg == '+' || *arg == '-')
+ arg++;
+ tokenize(arg);
+ *patprogp = patcompile(arg, 0, 0);
+ }
+ } else
+ patprogs = NULL;
+
+ if (OPT_ISSET(ops,'l') || OPT_ISSET(ops,'L') || OPT_ISSET(ops,'e')) {
+ /*
+ * With option 'l', list all features one per line with + or -.
+ * With option 'L', list as zmodload statement showing
+ * only options turned on.
+ * With both options, list as zmodload showing options
+ * to be turned both on and off.
+ */
+ Module m;
+ char **features, **fp, **arrset = NULL, **arrp = NULL;
+ int *enables = NULL, *ep;
+ char *param = OPT_ARG_SAFE(ops,'P');
+
+ m = find_module(modname, FINDMOD_ALIASP, NULL);
+ if (OPT_ISSET(ops,'a')) {
+ LinkNode ln;
+ /*
+ * If there are no autoloads defined, return status 1.
+ */
+ if (!m || !m->autoloads)
+ return 1;
+ if (OPT_ISSET(ops,'e')) {
+ for (fp = args; *fp; fp++) {
+ char *fstr = *fp;
+ int sense = 1;
+ if (*fstr == '+')
+ fstr++;
+ else if (*fstr == '-') {
+ fstr++;
+ sense = 0;
+ }
+ if ((linknodebystring(m->autoloads, fstr) != NULL) !=
+ sense)
+ return 1;
+ }
+ return 0;
+ }
+ if (param) {
+ arrp = arrset = (char **)zalloc(sizeof(char*) *
+ (countlinknodes(m->autoloads)+1));
+ } else if (OPT_ISSET(ops,'L')) {
+ printf("zmodload -aF %s%c", m->node.nam,
+ m->autoloads && firstnode(m->autoloads) ? ' ' : '\n');
+ arrp = NULL;
+ }
+ for (ln = firstnode(m->autoloads); ln; incnode(ln)) {
+ char *al = (char *)getdata(ln);
+ if (param)
+ *arrp++ = ztrdup(al);
+ else
+ printf("%s%c", al,
+ OPT_ISSET(ops,'L') && nextnode(ln) ? ' ' : '\n');
+ }
+ if (param) {
+ *arrp = NULL;
+ if (!setaparam(param, arrset))
+ return 1;
+ }
+ return 0;
+ }
+ if (!m || !m->u.handle || (m->node.flags & MOD_UNLOAD)) {
+ if (!OPT_ISSET(ops,'e'))
+ zwarnnam(nam, "module `%s' is not yet loaded", modname);
+ return 1;
+ }
+ if (features_module(m, &features)) {
+ if (!OPT_ISSET(ops,'e'))
+ zwarnnam(nam, "module `%s' does not support features",
+ m->node.nam);
+ return 1;
+ }
+ if (enables_module(m, &enables)) {
+ /* this shouldn't ever happen, so don't silence this error */
+ zwarnnam(nam, "error getting enabled features for module `%s'",
+ m->node.nam);
+ return 1;
+ }
+ for (arrp = args, iarg = 0; *arrp; arrp++, iarg++) {
+ char *arg = *arrp;
+ int on, found = 0;
+ if (*arg == '-') {
+ on = 0;
+ arg++;
+ } else if (*arg == '+') {
+ on = 1;
+ arg++;
+ } else
+ on = -1;
+ for (fp = features, ep = enables; *fp; fp++, ep++) {
+ if (patprogs ? pattry(patprogs[iarg], *fp) :
+ !strcmp(arg, *fp)) {
+ /* for -e, check given state, if any */
+ if (OPT_ISSET(ops,'e') && on != -1 &&
+ on != (*ep & 1))
+ return 1;
+ found++;
+ if (!patprogs)
+ break;
+ }
+ }
+ if (!found) {
+ if (!OPT_ISSET(ops,'e'))
+ zwarnnam(nam, patprogs ?
+ "module `%s' has no feature matching: `%s'" :
+ "module `%s' has no such feature: `%s'",
+ modname, *arrp);
+ return 1;
+ }
+ }
+ if (OPT_ISSET(ops,'e')) /* yep, everything we want exists */
+ return 0;
+ if (param) {
+ int arrlen = 0;
+ for (fp = features, ep = enables; *fp; fp++, ep++) {
+ if (OPT_ISSET(ops, 'L') && !OPT_ISSET(ops, 'l') &&
+ !*ep)
+ continue;
+ if (*args) {
+ char **argp;
+ for (argp = args, iarg = 0; *argp; argp++, iarg++) {
+ char *arg = *argp;
+ /* ignore +/- for consistency */
+ if (*arg == '+' || *arg == '-')
+ arg++;
+ if (patprogs ? pattry(patprogs[iarg], *fp) :
+ !strcmp(*fp, arg))
+ break;
+ }
+ if (!*argp)
+ continue;
+ }
+ arrlen++;
+ }
+ arrp = arrset = zalloc(sizeof(char *) * (arrlen+1));
+ } else if (OPT_ISSET(ops, 'L'))
+ printf("zmodload -F %s ", m->node.nam);
+ for (fp = features, ep = enables; *fp; fp++, ep++) {
+ char *onoff;
+ int term;
+ if (*args) {
+ char **argp;
+ for (argp = args, iarg = 0; *argp; argp++, iarg++) {
+ char *arg = *argp;
+ if (*arg == '+' || *arg == '-')
+ arg++;
+ if (patprogs ? pattry(patprogs[iarg], *fp) :
+ !strcmp(*fp, *argp))
+ break;
+ }
+ if (!*argp)
+ continue;
+ }
+ if (OPT_ISSET(ops, 'L') && !OPT_ISSET(ops, 'l')) {
+ if (!*ep)
+ continue;
+ onoff = "";
+ } else if (*ep) {
+ onoff = "+";
+ } else {
+ onoff = "-";
+ }
+ if (param) {
+ *arrp++ = bicat(onoff, *fp);
+ } else {
+ if (OPT_ISSET(ops, 'L') && fp[1]) {
+ term = ' ';
+ } else {
+ term = '\n';
+ }
+ printf("%s%s%c", onoff, *fp, term);
+ }
+ }
+ if (param) {
+ *arrp = NULL;
+ if (!setaparam(param, arrset))
+ return 1;
+ }
+ return 0;
+ } else if (OPT_ISSET(ops,'P')) {
+ zwarnnam(nam, "-P can only be used with -l or -L");
+ return 1;
+ } else if (OPT_ISSET(ops,'a')) {
+ if (OPT_ISSET(ops,'m')) {
+ zwarnnam(nam, "-m cannot be used with -a");
+ return 1;
+ }
+ /*
+ * With zmodload -aF, we always use the effect of -i.
+ * The thinking is that marking a feature for
+ * autoload is separate from enabling or disabling it.
+ * Arguably we could do this with the zmodload -ab method
+ * but I've kept it there for old time's sake.
+ * The decoupling has meant FEAT_IGNORE/-i also
+ * suppresses an error for attempting to remove an
+ * autoload when the feature is enabled, which used
+ * to be a hard error before.
+ */
+ return autofeatures(nam, modname, args, 0, FEAT_IGNORE);
+ }
+
+ fep = features =
+ (Feature_enables)zhalloc((arrlen(args)+1)*sizeof(*fep));
+
+ while (*args) {
+ fep->str = *args++;
+ fep->pat = patprogs ? *patprogs++ : NULL;
+ fep++;
+ }
+ fep->str = NULL;
+ fep->pat = NULL;
+
+ return require_module(modname, features, OPT_ISSET(ops,'s'));
+}
+
+
+/************************************************************************
+ * Generic feature support.
+ * These functions are designed to be called by modules.
+ ************************************************************************/
+
+/*
+ * Construct a features array out of the list of concrete
+ * features given, leaving space for any abstract features
+ * to be added by the module itself.
+ *
+ * Note the memory is from the heap.
+ */
+
+/**/
+mod_export char **
+featuresarray(UNUSED(Module m), Features f)
+{
+ int bn_size = f->bn_size, cd_size = f->cd_size;
+ int mf_size = f->mf_size, pd_size = f->pd_size;
+ int features_size = bn_size + cd_size + pd_size + mf_size + f->n_abstract;
+ Builtin bnp = f->bn_list;
+ Conddef cdp = f->cd_list;
+ MathFunc mfp = f->mf_list;
+ Paramdef pdp = f->pd_list;
+ char **features = (char **)zhalloc((features_size + 1) * sizeof(char *));
+ char **featurep = features;
+
+ while (bn_size--)
+ *featurep++ = dyncat("b:", (bnp++)->node.nam);
+ while (cd_size--) {
+ *featurep++ = dyncat((cdp->flags & CONDF_INFIX) ? "C:" : "c:",
+ cdp->name);
+ cdp++;
+ }
+ while (mf_size--)
+ *featurep++ = dyncat("f:", (mfp++)->name);
+ while (pd_size--)
+ *featurep++ = dyncat("p:", (pdp++)->name);
+
+ features[features_size] = NULL;
+ return features;
+}
+
+/*
+ * Return the current set of enables for the features in a
+ * module using heap memory. Leave space for abstract
+ * features. The array is not zero terminated.
+ */
+/**/
+mod_export int *
+getfeatureenables(UNUSED(Module m), Features f)
+{
+ int bn_size = f->bn_size, cd_size = f->cd_size;
+ int mf_size = f->mf_size, pd_size = f->pd_size;
+ int features_size = bn_size + cd_size + mf_size + pd_size + f->n_abstract;
+ Builtin bnp = f->bn_list;
+ Conddef cdp = f->cd_list;
+ MathFunc mfp = f->mf_list;
+ Paramdef pdp = f->pd_list;
+ int *enables = zhalloc(sizeof(int) * features_size);
+ int *enablep = enables;
+
+ while (bn_size--)
+ *enablep++ = ((bnp++)->node.flags & BINF_ADDED) ? 1 : 0;
+ while (cd_size--)
+ *enablep++ = ((cdp++)->flags & CONDF_ADDED) ? 1 : 0;
+ while (mf_size--)
+ *enablep++ = ((mfp++)->flags & MFF_ADDED) ? 1 : 0;
+ while (pd_size--)
+ *enablep++ = (pdp++)->pm ? 1 : 0;
+
+ return enables;
+}
+
+/*
+ * Add or remove the concrete features passed in arguments,
+ * depending on the corresponding element of the array e.
+ * If e is NULL, disable everything.
+ * Return 0 for success, 1 for failure; does not attempt
+ * to imitate the return values of addbuiltins() etc.
+ * Any failure in adding a requested feature is an
+ * error.
+ */
+
+/**/
+mod_export int
+setfeatureenables(Module m, Features f, int *e)
+{
+ int ret = 0;
+
+ if (f->bn_size) {
+ if (setbuiltins(m->node.nam, f->bn_list, f->bn_size, e))
+ ret = 1;
+ if (e)
+ e += f->bn_size;
+ }
+ if (f->cd_size) {
+ if (setconddefs(m->node.nam, f->cd_list, f->cd_size, e))
+ ret = 1;
+ if (e)
+ e += f->cd_size;
+ }
+ if (f->mf_size) {
+ if (setmathfuncs(m->node.nam, f->mf_list, f->mf_size, e))
+ ret = 1;
+ if (e)
+ e += f->mf_size;
+ }
+ if (f->pd_size) {
+ if (setparamdefs(m->node.nam, f->pd_list, f->pd_size, e))
+ ret = 1;
+ if (e)
+ e += f->pd_size;
+ }
+ return ret;
+}
+
+/*
+ * Convenient front-end to get or set features which
+ * can be used in a module enables_() function.
+ */
+
+/**/
+mod_export int
+handlefeatures(Module m, Features f, int **enables)
+{
+ if (!enables || *enables)
+ return setfeatureenables(m, f, enables ? *enables : NULL);
+ *enables = getfeatureenables(m, f);
+ return 0;
+}
+
+/*
+ * Ensure module "modname" is providing feature with "prefix"
+ * and "feature" (e.g. "b:", "limit"). If feature is NULL,
+ * ensure all features are loaded (used for compatibility
+ * with the pre-feature autoloading behaviour).
+ *
+ * This will usually be called from the main shell to handle
+ * loading of an autoloadable feature.
+ *
+ * Returns 0 on success, 1 for error in module, 2 for error
+ * setting the feature. However, this isn't actually all
+ * that useful for testing immediately on an autoload since
+ * it could be a failure to autoload a different feature
+ * from the one we want. We could fix this but it's
+ * possible to test other ways.
+ */
+
+/**/
+mod_export int
+ensurefeature(const char *modname, const char *prefix, const char *feature)
+{
+ char *f;
+ struct feature_enables features[2];
+
+ if (!feature)
+ return require_module(modname, NULL, 0);
+ f = dyncat(prefix, feature);
+
+ features[0].str = f;
+ features[0].pat = NULL;
+ features[1].str = NULL;
+ features[1].pat = NULL;
+ return require_module(modname, features, 0);
+}
+
+/*
+ * Add autoloadable features for a given module.
+ */
+
+/**/
+int
+autofeatures(const char *cmdnam, const char *module, char **features,
+ int prefchar, int defflags)
+{
+ int ret = 0, subret;
+ Module defm, m;
+ char **modfeatures = NULL;
+ int *modenables = NULL;
+ if (module) {
+ defm = (Module)find_module(module,
+ FINDMOD_ALIASP|FINDMOD_CREATE, NULL);
+ if ((defm->node.flags & MOD_LINKED) ? defm->u.linked :
+ defm->u.handle) {
+ (void)features_module(defm, &modfeatures);
+ (void)enables_module(defm, &modenables);
+ }
+ } else
+ defm = NULL;
+
+ for (; *features; features++) {
+ char *fnam, *typnam, *feature;
+ int add, fchar, flags = defflags;
+ autofeaturefn_t fn;
+
+ if (prefchar) {
+ /*
+ * "features" is list of bare features with no
+ * type prefix; prefchar gives type character.
+ */
+ add = 1; /* unless overridden by flag */
+ fchar = prefchar;
+ fnam = *features;
+ feature = zhalloc(strlen(fnam) + 3);
+ sprintf(feature, "%c:%s", fchar, fnam);
+ } else {
+ 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;
+ continue;
+ }
+ fnam = feature + 2;
+ fchar = feature[0];
+ }
+ if (flags & FEAT_REMOVE)
+ add = 0;
+
+ switch (fchar) {
+ case 'b':
+ fn = add ? add_autobin : del_autobin;
+ typnam = "builtin";
+ break;
+
+ case 'C':
+ flags |= FEAT_INFIX;
+ /* FALLTHROUGH */
+ case 'c':
+ fn = add ? add_autocond : del_autocond;
+ typnam = "condition";
+ break;
+
+ case 'f':
+ fn = add ? add_automathfunc : del_automathfunc;
+ typnam = "math function";
+ break;
+
+ case 'p':
+ fn = add ? add_autoparam : del_autoparam;
+ typnam = "parameter";
+ break;
+
+ default:
+ zwarnnam(cmdnam, "bad autoloadable feature type: `%c'",
+ fchar);
+ ret = 1;
+ continue;
+ }
+
+ if (strchr(fnam, '/')) {
+ zwarnnam(cmdnam, "%s: `/' is illegal in a %s", fnam, typnam);
+ ret = 1;
+ continue;
+ }
+
+ if (!module) {
+ /*
+ * Traditional un-autoload syntax doesn't tell us
+ * which module this came from.
+ */
+ int i;
+ for (i = 0, m = NULL; !m && i < modulestab->hsize; i++) {
+ for (m = (Module)modulestab->nodes[i]; m;
+ m = (Module)m->node.next) {
+ if (m->autoloads &&
+ linknodebystring(m->autoloads, feature))
+ break;
+ }
+ }
+ if (!m) {
+ if (!(flags & FEAT_IGNORE)) {
+ ret = 1;
+ zwarnnam(cmdnam, "%s: no such %s", fnam, typnam);
+ }
+ continue;
+ }
+ } else
+ m = defm;
+
+ subret = 0;
+ if (add) {
+ char **ptr;
+ if (modfeatures) {
+ /*
+ * If the module is already available, check that
+ * it does in fact provide the necessary feature.
+ */
+ for (ptr = modfeatures; *ptr; ptr++)
+ if (!strcmp(*ptr, feature))
+ break;
+ if (!*ptr) {
+ zwarnnam(cmdnam, "module `%s' has no such feature: `%s'",
+ m->node.nam, feature);
+ ret = 1;
+ continue;
+ }
+ /*
+ * If the feature is already provided by the module, there's
+ * nothing more to do.
+ */
+ if (modenables[ptr-modfeatures])
+ continue;
+ /*
+ * Otherwise, marking it for autoload will do the
+ * right thing when the feature is eventually used.
+ */
+ }
+ if (!m->autoloads) {
+ m->autoloads = znewlinklist();
+ zaddlinknode(m->autoloads, ztrdup(feature));
+ } else {
+ /* Insert in lexical order */
+ LinkNode ln, prev = (LinkNode)m->autoloads;
+ while ((ln = nextnode(prev))) {
+ int cmp = strcmp(feature, (char *)getdata(ln));
+ if (cmp == 0) {
+ /* Already there. Never an error. */
+ break;
+ }
+ if (cmp < 0) {
+ zinsertlinknode(m->autoloads, prev,
+ ztrdup(feature));
+ break;
+ }
+ prev = ln;
+ }
+ if (!ln)
+ zaddlinknode(m->autoloads, ztrdup(feature));
+ }
+ } else if (m->autoloads) {
+ LinkNode ln;
+ if ((ln = linknodebystring(m->autoloads, feature)))
+ zsfree((char *)remnode(m->autoloads, ln));
+ else {
+ /*
+ * With -i (or zmodload -Fa), removing an autoload
+ * that's not there is not an error.
+ */
+ subret = (flags & FEAT_IGNORE) ? -2 : 2;
+ }
+ }
+
+ if (subret == 0)
+ subret = fn(module, fnam, flags);
+
+ if (subret != 0) {
+ /* -2 indicates not an error, just skip running fn() */
+ if (subret != -2)
+ 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;
+}