diff options
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/module.c')
| -rw-r--r-- | dotfiles/system/.zsh/modules/Src/module.c | 3641 | 
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; +} | 
