From b0c5f09169ac31855ebf0e93772bb57b9635b380 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 28 May 2007 22:57:39 +0000 Subject: see 23479: add initial features support for modules --- Src/module.c | 3805 ++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 2219 insertions(+), 1586 deletions(-) (limited to 'Src/module.c') diff --git a/Src/module.c b/Src/module.c index ffa659efc..8f5afca83 100644 --- a/Src/module.c +++ b/Src/module.c @@ -35,6 +35,20 @@ /**/ LinkList linkedmodules; +/* $module_path ($MODULE_PATH) */ + +/**/ +char **module_path; + +/* List of modules */ + +/**/ +mod_export LinkList modules; + + +/************************************************************************ + * 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 * @@ -47,6 +61,24 @@ 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)) @@ -68,12 +100,21 @@ finish_(UNUSED(Module m)) return 0; } + +/************************************************************************ + * Module utility functions + ************************************************************************/ + /* This registers a builtin module. */ /**/ void -register_module(char *n, Module_func setup, Module_func boot, - Module_func cleanup, Module_func finish) +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; @@ -81,6 +122,8 @@ register_module(char *n, Module_func setup, Module_func boot, m->name = ztrdup(n); m->setup = setup; + m->features = features; + m->enables = enables; m->boot = boot; m->cleanup = cleanup; m->finish = finish; @@ -124,6 +167,12 @@ module_linked(char const *name) 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 * @@ -142,13 +191,81 @@ addbuiltin(Builtin b) return 0; } -/* Add multiple builtins. binl points to a table of `size' builtin * - * structures. Those for which (.flags & BINF_ADDED) is false are to be * - * added; that flag is set if they succeed. If any fail, an error * - * message is printed, using nam as the leading name. Returns 1 if all * - * additions succeed, 2 if some succeed and some fail, and 0 if all (and * - * at least 1) fail. The usual usage in a boot_*() function would be * - * return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); */ +/* Define an autoloadable builtin. It returns 0 on success, or 1 on * + * failure. The only possible cause of failure is that a builtin * + * with the specified name already exists. */ + +/**/ +int +add_autobin(char *nam, char *module) +{ + Builtin bn = zshcalloc(sizeof(*bn)); + bn->node.nam = ztrdup(nam); + bn->optstr = ztrdup(module); + return addbuiltin(bn); +} + +/* Remove the builtin added previously by addbuiltin(). Returns * + * zero on succes and -1 if there is no builtin with that name. */ + +/**/ +int +deletebuiltin(char *nam) +{ + Builtin bn; + + bn = (Builtin) builtintab->removenode(builtintab, nam); + if (!bn) + return -1; + builtintab->freenode(&bn->node); + return 0; +} + +/**/ +mod_export int +setbuiltins(char const *nam, Builtin binl, int size, int *e) +{ + int hads = 0, hadf = 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); + hadf = 1; + } else { + b->node.flags |= BINF_ADDED; + hads = 2; + } + } else { + if (!(b->node.flags & BINF_ADDED)) + continue; + if (deletebuiltin(b->node.nam)) { + zwarnnam(nam, "builtin `%s' already deleted", b->node.nam); + hadf = 1; + } else { + hads = 2; + b->node.flags &= ~BINF_ADDED; + } + } + } + return hadf ? hads : 1; +} + +/* + * Add multiple builtins. binl points to a table of `size' builtin + * structures. Those for which (.flags & BINF_ADDED) is false are to be + * added; that flag is set if they succeed. + * + * If any fail, an error message is printed, using nam as the leading name. + * Returns 1 if all additions succeed, 2 if some succeed and some fail, and 0 + * if all (and at least 1) fail. + * + * This should not be used from a module; instead, use handlefeatures(). + */ /**/ mod_export int @@ -171,6 +288,11 @@ addbuiltins(char const *nam, Builtin binl, int size) return hadf ? hads : 1; } + +/************************************************************************ + * Function wrappers. + ************************************************************************/ + /* The list of function wrappers defined. */ /**/ @@ -208,75 +330,6 @@ addwrapper(Module m, FuncWrap w) return 0; } -/* $module_path ($MODULE_PATH) */ - -/**/ -char **module_path; - -/* List of modules */ - -/**/ -mod_export LinkList modules; - -/* Define an autoloadable builtin. It returns 0 on success, or 1 on * - * failure. The only possible cause of failure is that a builtin * - * with the specified name already exists. */ - -/**/ -int -add_autobin(char *nam, char *module) -{ - Builtin bn = zshcalloc(sizeof(*bn)); - bn->node.nam = ztrdup(nam); - bn->optstr = ztrdup(module); - return addbuiltin(bn); -} - -/* Remove the builtin added previously by addbuiltin(). Returns * - * zero on succes and -1 if there is no builtin with that name. */ - -/**/ -int -deletebuiltin(char *nam) -{ - Builtin bn; - - bn = (Builtin) builtintab->removenode(builtintab, nam); - if (!bn) - return -1; - builtintab->freenode(&bn->node); - return 0; -} - -/* Delete multiple builtins. binl points to a table of `size' builtin * - * structures. Those for which (.flags & BINF_ADDED) is true are to be * - * deleted; that flag is cleared. If any fail, an error message is * - * printed, using nam as the leading name. Returns 1 if all deletions * - * succeed, 2 if some succeed and some fail, and 0 if all (and at least * - * 1) fail. In normal use, from a cleanup_*() function, this return * - * value would be ignored -- the only cause of failure would be that a * - * wayward module had deleted our builtin without telling us. */ - -/**/ -mod_export int -deletebuiltins(char const *nam, Builtin binl, int size) -{ - int hads = 0, hadf = 0, n; - - for(n = 0; n < size; n++) { - Builtin b = &binl[n]; - if(!(b->node.flags & BINF_ADDED)) - continue; - if(deletebuiltin(b->node.nam)) { - zwarnnam(nam, "builtin `%s' already deleted", b->node.nam); - hadf = 1; - } else - hads = 2; - b->node.flags &= ~BINF_ADDED; - } - return hadf ? hads : 1; -} - /* This removes the given wrapper definition from the list. Returned is * * one in case of error and zero otherwise. */ @@ -305,1925 +358,2505 @@ deletewrapper(Module m, FuncWrap w) return 1; } -/**/ -#ifdef DYNAMIC -/**/ -#ifdef AIXDYNAMIC +/************************************************************************ + * Conditions. + ************************************************************************/ -#include +/* The list of module-defined conditions. */ -static char *dlerrstr[256]; +/**/ +mod_export Conddef condtab; -static void * -load_and_bind(const char *fn) -{ - void *ret = (void *) load((char *) fn, L_NOAUTODEFER, NULL); +/* 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. */ - if (ret) { - LinkNode node; - int err = loadbind(0, (void *) addbuiltin, ret); - for (node = firstnode(modules); !err && node; incnode(node)) { - Module m = (Module) getdata(node); - if (!(m->flags & MOD_ALIAS) && - m->u.handle && !(m->flags & MOD_LINKED)) - err |= loadbind(0, m->u.handle, ret); - } +/**/ +Conddef +getconddef(int inf, char *name, int autol) +{ + Conddef p; + int f = 1; - if (err) { - loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr)); - unload(ret); - ret = NULL; + do { + for (p = condtab; p; p = p->next) { + if ((!!inf == !!(p->flags & CONDF_INFIX)) && + !strcmp(name, p->name)) + break; } - } else - loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr)); - - return ret; + 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)load_module_silence(p->module, NULL, 0); + f = 0; + p = NULL; + } else { + deleteconddef(p); + return NULL; + } + } else + break; + } while (!p); + return p; } -#define dlopen(X,Y) load_and_bind(X) -#define dlclose(X) unload(X) -#define dlerror() (dlerrstr[0]) +/* + * 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. + */ /**/ -#else - -#ifdef HAVE_DLFCN_H -# if defined(HAVE_DL_H) && defined(HPUXDYNAMIC) -# include -# else -# include -# endif -#else -# ifdef HAVE_DL_H -# include -# define RTLD_LAZY BIND_DEFERRED -# define RTLD_GLOBAL DYNAMIC_PATH -# else -# include -# include -# include -# endif -#endif +static int +addconddef(Conddef c) +{ + Conddef p = getconddef((c->flags & CONDF_INFIX), c->name, 0); -/**/ -#ifdef HPUXDYNAMIC -# define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) -# define dlclose(handle) shl_unload((shl_t)(handle)) + if (p) { + if (!p->module || (p->flags & CONDF_ADDED)) + return 1; + /* There is an autoload definition. */ -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; + deleteconddef(p); + } + c->next = condtab; + condtab = c; + return 0; } -# define dlsym(handle,name) hpux_dlsym(handle,name) -# define dlerror() 0 -#else -# ifndef HAVE_DLCLOSE -# define dlclose(X) ((X), 0) -# endif +/* This removes the given condition definition from the list(s). If this * + * is a definition for a autoloaded condition, the memory is freed. */ + /**/ -#endif - -#ifdef DLSYM_NEEDS_UNDERSCORE -# define STR_SETUP "_setup_" -# define STR_BOOT "_boot_" -# define STR_CLEANUP "_cleanup_" -# define STR_FINISH "_finish_" -#else /* !DLSYM_NEEDS_UNDERSCORE */ -# define STR_SETUP "setup_" -# define STR_BOOT "boot_" -# define STR_CLEANUP "cleanup_" -# define STR_FINISH "finish_" -#endif /* !DLSYM_NEEDS_UNDERSCORE */ +int +deleteconddef(Conddef c) +{ + Conddef p, q; -/**/ -#endif /* !AIXDYNAMIC */ + for (p = condtab, q = NULL; p && p != c; q = p, p = p->next); -#ifndef RTLD_LAZY -# define RTLD_LAZY 1 -#endif -#ifndef RTLD_GLOBAL -# define RTLD_GLOBAL 0 -#endif + 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; +} /**/ -static void * -try_load_module(char const *name) +mod_export int +setconddefs(char const *nam, Conddef c, int size, int *e) { - char buf[PATH_MAX + 1]; - char **pp; - void *ret = NULL; - int l; + int hads = 0, hadf = 0; - l = 1 + strlen(name) + 1 + strlen(DL_EXT); - for (pp = module_path; !ret && *pp; pp++) { - if (l + (**pp ? strlen(*pp) : 1) > PATH_MAX) - continue; - sprintf(buf, "%s/%s.%s", **pp ? *pp : ".", name, DL_EXT); - ret = dlopen(unmeta(buf), RTLD_LAZY | RTLD_GLOBAL); + 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); + hadf = 1; + } else { + c->flags |= CONDF_ADDED; + hads = 2; + } + } else { + if (!(c->flags & CONDF_ADDED)) { + c++; + continue; + } + if (deleteconddef(c)) { + zwarnnam(nam, "condition `%s' already deleted", c->name); + hadf = 1; + } else { + c->flags &= ~CONDF_ADDED; + hads = 2; + } + } + c++; } - - return ret; + return hadf ? hads : 1; } +/* This adds a definition for autoloading a module for a condition. */ + /**/ -static void * -do_load_module(char const *name, int silent) +int +add_autocond(char *nam, int inf, char *module) { - void *ret; + Conddef c = (Conddef) zalloc(sizeof(*c)); - ret = try_load_module(name); - if (!ret && !silent) - zwarn("failed to load module: %s", name); - return ret; + c->name = ztrdup(nam); + c->flags = (inf ? CONDF_INFIX : 0); + c->module = ztrdup(module); + + if (addconddef(c)) { + zsfree(c->name); + zsfree(c->module); + zfree(c, sizeof(*c)); + + return 1; + } + return 0; } -/**/ -#else /* !DYNAMIC */ -/**/ -static void * -do_load_module(char const *name, int silent) -{ - if (!silent) - zwarn("failed to load module: %s", name); +/************************************************************************ + * Hook functions. + ************************************************************************/ - return NULL; -} +/* This list of hook functions defined. */ /**/ -#endif /* !DYNAMIC */ +Hookdef hooktab; + +/* Find a hook definition given the name. */ -/* - * Find a module in the list. - * If aliasp is non-zero, resolve any aliases to the underlying module. - * If namep is set, this is set to point to the last alias value resolved, - * even if that module was not loaded. or the module name if no aliases. - * Hence this is always the physical module to load in a chain of aliases. - * Return NULL if the module named is not stored as a structure, or if we were - * resolving aliases and the final module named is not stored as a - * structure. - * - * TODO: now we have aliases, there may be some merit in using a hash - * table instead of a linked list. - */ /**/ -static LinkNode -find_module(const char *name, int aliasp, const char **namep) +Hookdef +gethookdef(char *n) { - Module m; - LinkNode node; + Hookdef p; - for (node = firstnode(modules); node; incnode(node)) { - m = (Module) getdata(node); - if (!strcmp(m->nam, name)) { - if (aliasp && (m->flags & MOD_ALIAS)) { - if (namep) - *namep = m->u.alias; - return find_module(m->u.alias, 1, namep); - } - if (namep) - *namep = m->nam; - return node; - } - } + for (p = hooktab; p; p = p->next) + if (!strcmp(n, p->name)) + return p; return NULL; } -/* - * Unlink and free a module node from the linked list. - */ +/* This adds the given hook definition. The return value is zero on * + * success and 1 on failure. */ /**/ -static void -delete_module(LinkNode node) +int +addhookdef(Hookdef h) { - Module m = (Module) remnode(modules, node); + if (gethookdef(h->name)) + return 1; - if (m->flags & MOD_ALIAS) - zsfree(m->u.alias); - zsfree(m->nam); - if (m->deps) - freelinklist(m->deps, freestr); - zfree(m, sizeof(*m)); + h->next = hooktab; + hooktab = h; + h->funcs = znewlinklist(); + + return 0; } +/* This adds multiple hook definitions. This is like addbuiltins(). */ + /**/ mod_export int -module_loaded(const char *name) +addhookdefs(char const *nam, Hookdef h, int size) { - LinkNode node; - Module m; + int hads = 0, hadf = 0; - return ((node = find_module(name, 1, NULL)) && - (m = ((Module) getdata(node)))->u.handle && - !(m->flags & MOD_UNLOAD)); + while (size--) { + if (addhookdef(h)) { + zwarnnam(nam, "name clash when adding hook `%s'", h->name); + hadf = 1; + } else + hads = 2; + h++; + } + return hadf ? hads : 1; } -/* - * 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. - */ +/* Delete hook definitions. */ /**/ -#ifdef DYNAMIC +int +deletehookdef(Hookdef h) +{ + Hookdef p, q; -/**/ -#ifdef AIXDYNAMIC + 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; +} /**/ -static int -dyn_setup_module(Module m) +mod_export int +deletehookdefs(UNUSED(char const *nam), Hookdef h, int size) { - return ((int (*)_((int,Module))) m->u.handle)(0, m); + while (size--) { + deletehookdef(h); + h++; + } + return 1; } +/* Add a function to a hook. */ + /**/ -static int -dyn_boot_module(Module m) +int +addhookdeffunc(Hookdef h, Hookfn f) { - return ((int (*)_((int,Module))) m->u.handle)(1, m); + zaddlinknode(h->funcs, (void *) f); + + return 0; } /**/ -static int -dyn_cleanup_module(Module m) +mod_export int +addhookfunc(char *n, Hookfn f) { - return ((int (*)_((int,Module))) m->u.handle)(2, m); + Hookdef h = gethookdef(n); + + if (h) + return addhookdeffunc(h, f); + return 1; } +/* Delete a function from a hook. */ + /**/ -static int -dyn_finish_module(Module m) +int +deletehookdeffunc(Hookdef h, Hookfn f) { - return ((int (*)_((int,Module))) m->u.handle)(3, m); + LinkNode p; + + for (p = firstnode(h->funcs); p; incnode(p)) + if (f == (Hookfn) getdata(p)) { + remnode(h->funcs, p); + return 0; + } + return 1; } /**/ -#else - -static Module_func -module_func(Module m, char *name) -{ -#ifdef DYNAMIC_NAME_CLASH_OK - return (Module_func) dlsym(m->u.handle, name); -#else /* !DYNAMIC_NAME_CLASH_OK */ - VARARR(char, buf, strlen(name) + strlen(m->nam)*2 + 1); - char const *p; - char *q; - strcpy(buf, name); - q = strchr(buf, 0); - for(p = m->nam; *p; p++) { - if(*p == '/') { - *q++ = 'Q'; - *q++ = 's'; - } else if(*p == '_') { - *q++ = 'Q'; - *q++ = 'u'; - } else if(*p == 'Q') { - *q++ = 'Q'; - *q++ = 'q'; - } else - *q++ = *p; - } - *q = 0; - return (Module_func) dlsym(m->u.handle, buf); -#endif /* !DYNAMIC_NAME_CLASH_OK */ -} - -/**/ -static int -dyn_setup_module(Module m) +mod_export int +deletehookfunc(char *n, Hookfn f) { - Module_func fn = module_func(m, STR_SETUP); + Hookdef h = gethookdef(n); - if (fn) - return fn(m); - zwarnnam(m->nam, "no setup function"); + if (h) + return deletehookdeffunc(h, f); return 1; } +/* Run the function(s) for a hook. */ + /**/ -static int -dyn_boot_module(Module m) +mod_export int +runhookdef(Hookdef h, void *d) { - Module_func fn = module_func(m, STR_BOOT); + if (empty(h->funcs)) { + if (h->def) + return h->def(h, d); + return 0; + } else if (h->flags & HOOKF_ALL) { + LinkNode p; + int r; - if(fn) - return fn(m); - zwarnnam(m->nam, "no boot function"); - return 1; + 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); } /**/ -static int -dyn_cleanup_module(Module m) +int +runhook(char *n, void *d) { - Module_func fn = module_func(m, STR_CLEANUP); + Hookdef h = gethookdef(n); - if(fn) - return fn(m); - zwarnnam(m->nam, "no cleanup function"); - return 1; + if (h) + return runhookdef(h, d); + return 0; } -/* Note that this function does more than just calling finish_foo(), * - * it really unloads the module. */ -/**/ +/************************************************************************ + * Shell parameters. + ************************************************************************/ + static int -dyn_finish_module(Module m) +checkaddparam(char *nam) { - Module_func fn = module_func(m, STR_FINISH); - int r; + Param pm; - if (fn) - r = fn(m); - else { - zwarnnam(m->nam, "no finish function"); - r = 1; + if (!(pm = (Param) gethashnode2(paramtab, nam))) + return 0; + + if (pm->level || !(pm->node.flags & PM_AUTOLOAD)) { + zwarn("Can't add module parameter `%s': %s", + nam, pm->level ? + "local parameter exists" : + "parameter already exists"); + return 1; } - dlclose(m->u.handle); - return r; + + unsetparam_pm(pm, 0, 1); + return 0; } -/**/ -#endif /* !AIXDYNAMIC */ +/* This adds the given parameter definition. The return value is zero on * + * success and 1 on failure. */ /**/ -static int -setup_module(Module m) +int +addparamdef(Paramdef d) { - return ((m->flags & MOD_LINKED) ? - (m->u.linked->setup)(m) : dyn_setup_module(m)); -} + Param pm; -/**/ -static int -boot_module(Module m) -{ - return ((m->flags & MOD_LINKED) ? - (m->u.linked->boot)(m) : dyn_boot_module(m)); + if (checkaddparam(d->name)) + 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_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. */ + /**/ -static int -cleanup_module(Module m) +int +deleteparamdef(Paramdef d) { - return ((m->flags & MOD_LINKED) ? - (m->u.linked->cleanup)(m) : dyn_cleanup_module(m)); + 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; } /**/ -static int -finish_module(Module m) +mod_export int +setparamdefs(char const *nam, Paramdef d, int size, int *e) { - return ((m->flags & MOD_LINKED) ? - (m->u.linked->finish)(m) : dyn_finish_module(m)); + int hads = 0, hadf = 0; + + while (size--) { + if (e && *e++) { + if (d->pm) { + d++; + continue; + } + if (addparamdef(d)) { + zwarnnam(nam, "error when adding parameter `%s'", d->name); + hadf = 1; + } else { + hads = 2; + } + } else { + if (!d->pm) { + d++; + continue; + } + if (deleteparamdef(d)) { + zwarnnam(nam, "parameter `%s' already deleted", d->name); + hadf = 1; + } else { + hads = 2; + } + } + d++; + } + return hadf ? hads : 1; } -/**/ -#else /* !DYNAMIC */ +/* This adds a definition for autoloading a module for a parameter. */ /**/ -static int -setup_module(Module m) +void +add_autoparam(char *nam, char *module) { - return ((m->flags & MOD_LINKED) ? (m->u.linked->setup)(m) : 1); + Param pm; + + queue_signals(); + if (checkaddparam(nam)) { + unqueue_signals(); + return; + } + + pm = setsparam(nam, ztrdup(module)); + + pm->node.flags |= PM_AUTOLOAD; + unqueue_signals(); } + +/************************************************************************ + * Math functions. + ************************************************************************/ + +/* List of math functions. */ + /**/ -static int -boot_module(Module m) -{ - return ((m->flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1); -} +MathFunc mathfuncs; /**/ -static int -cleanup_module(Module m) +void +removemathfunc(MathFunc previous, MathFunc current) { - return ((m->flags & MOD_LINKED) ? (m->u.linked->cleanup)(m) : 1); + if (previous) + previous->next = current->next; + else + mathfuncs = current->next; + + zsfree(current->name); + zsfree(current->module); + zfree(current, sizeof(*current)); } /**/ -static int -finish_module(Module m) +MathFunc +getmathfunc(char *name, int autol) { - return ((m->flags & MOD_LINKED) ? (m->u.linked->finish)(m) : 1); -} + MathFunc p, q = NULL; -/**/ -#endif /* !DYNAMIC */ + 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); -/**/ -static int -modname_ok(char const *p) -{ - do { - p = itype_end(p, IIDENT, 0); - if (!*p) - return 1; - } while(*p++ == '/'); - return 0; + removemathfunc(q, p); + + (void)load_module_silence(n, NULL, 0); + + return getmathfunc(name, 0); + } + return p; + } + + return NULL; } /**/ -mod_export int -load_module(char const *name) +static int +addmathfunc(MathFunc f) { - return load_module_silence(name, 0); -} + 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; +} /**/ mod_export int -load_module_silence(char const *name, int silent) +deletemathfunc(MathFunc f) { - Module m; - void *handle = NULL; - Linkedmod linked; - LinkNode node, n; - int set; + MathFunc p, q; - if (!modname_ok(name)) { - if (!silent) - zerr("invalid module name `%s'", name); - return 0; - } - /* - * The following function call may alter name to the final name in a - * chain of aliases. This makes sure the actual module loaded - * is the right one. - */ - queue_signals(); - if (!(node = find_module(name, 1, &name))) { - if (!(linked = module_linked(name)) && - !(handle = do_load_module(name, silent))) { - unqueue_signals(); - return 0; - } - m = zshcalloc(sizeof(*m)); - m->nam = ztrdup(name); - if (handle) { - m->u.handle = handle; - m->flags |= MOD_SETUP; - } else { - m->u.linked = linked; - m->flags |= MOD_SETUP | MOD_LINKED; - } - node = zaddlinknode(modules, m); + for (p = mathfuncs, q = NULL; p && p != f; q = p, p = p->next); - if ((set = setup_module(m)) || boot_module(m)) { - if (!set) - finish_module(m); - delete_module(node); - unqueue_signals(); - return 0; - } - m->flags |= MOD_INIT_S | MOD_INIT_B; - m->flags &= ~MOD_SETUP; - unqueue_signals(); - return 1; - } - m = (Module) getdata(node); - if (m->flags & MOD_SETUP) { - unqueue_signals(); - return 1; - } - if (m->flags & MOD_UNLOAD) - m->flags &= ~MOD_UNLOAD; - else if ((m->flags & MOD_LINKED) ? m->u.linked : m->u.handle) { - unqueue_signals(); - return 1; - } - if (m->flags & MOD_BUSY) { - zerr("circular dependencies for module %s", name); - return 0; - } - m->flags |= MOD_BUSY; - if (m->deps) - for (n = firstnode(m->deps); n; incnode(n)) - if (!load_module_silence((char *) getdata(n), silent)) { - m->flags &= ~MOD_BUSY; - unqueue_signals(); - return 0; - } - m->flags &= ~MOD_BUSY; - if (!m->u.handle) { - handle = NULL; - if (!(linked = module_linked(name)) && - !(handle = do_load_module(name, silent))) { - unqueue_signals(); - return 0; - } - if (handle) { - m->u.handle = handle; - m->flags |= MOD_SETUP; - } else { - m->u.linked = linked; - m->flags |= MOD_SETUP | MOD_LINKED; - } - if (setup_module(m)) { - if (handle) - m->u.handle = NULL; - else - m->u.linked = NULL; - m->flags &= ~MOD_SETUP; - unqueue_signals(); - return 0; - } - m->flags |= MOD_INIT_S; - } - m->flags |= MOD_SETUP; - if (boot_module(m)) { - finish_module(m); - if (m->flags & MOD_LINKED) - m->u.linked = NULL; + if (p) { + if (q) + q->next = f->next; else - m->u.handle = NULL; - m->flags &= ~MOD_SETUP; - unqueue_signals(); + 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; } - m->flags |= MOD_INIT_B; - m->flags &= ~MOD_SETUP; - unqueue_signals(); - return 1; + return -1; } -/* This ensures that the module with the name given as the second argument - * is loaded. - * The third argument should be non-zero if the function should complain - * about trying to load a module with a full path name in restricted mode. - * The last argument should be non-zero if this function should signal an - * error if the module is already loaded. - * The return value is non-zero if the module was found or loaded. */ - /**/ mod_export int -require_module(char *nam, const char *module, UNUSED(int res), int test) +setmathfuncs(char const *nam, MathFunc f, int size, int *e) { - Module m = NULL; - LinkNode node; - int ret = 1; + int hads = 0, hadf = 0; - /* Resolve aliases and actual loadable module as for load_module */ - queue_signals(); - node = find_module(module, 1, &module); - if (node && (m = ((Module) getdata(node)))->u.handle && - !(m->flags & MOD_UNLOAD)) { - if (test) { - unqueue_signals(); - zwarnnam(nam, "module %s already loaded.", module); - return 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); + hadf = 1; + } else { + f->flags |= MFF_ADDED; + hads = 2; + } + } else { + if (!(f->flags & MFF_ADDED)) { + f++; + continue; + } + if (deletemathfunc(f)) { + zwarnnam(nam, "math function `%s' already deleted", f->name); + hadf = 1; + } else { + f->flags &= ~MFF_ADDED; + hads = 2; + } } - } else - ret = load_module_silence(module, 0); - unqueue_signals(); - - return ret; + f++; + } + return hadf ? hads : 1; } /**/ -void -add_dep(const char *name, char *from) +int +add_automathfunc(char *nam, char *module) { - LinkNode node; - Module m; + MathFunc f = (MathFunc) zalloc(sizeof(*f)); - /* - * If we were passed an alias, we must resolve it to a final - * module name (and maybe add the corresponding struct), since otherwise - * we would need to check all modules to see if they happen - * to be aliased to the same thing to implement dependencies properly. - * - * This should mean that an attempt to add an alias which would - * have the same name as a module which has dependencies is correctly - * rejected, because then the module named already exists as a non-alias. - * Better make sure. (There's no problem making a an alias which - * *points* to a module with dependencies, of course.) - */ - if (!(node = find_module(name, 1, &name))) { - m = zshcalloc(sizeof(*m)); - m->nam = ztrdup(name); - zaddlinknode(modules, m); - } else - m = (Module) getdata(node); - if (!m->deps) - m->deps = znewlinklist(); - for (node = firstnode(m->deps); - node && strcmp((char *) getdata(node), from); - incnode(node)); - if (!node) - zaddlinknode(m->deps, ztrdup(from)); -} + f->name = ztrdup(nam); + f->module = ztrdup(module); + f->flags = 0; -/**/ -static void -autoloadscan(HashNode hn, int printflags) -{ - Builtin bn = (Builtin) hn; + if (addmathfunc(f)) { + zsfree(f->name); + zsfree(f->module); + zfree(f, sizeof(*f)); - 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(')'); - } + return 1; } - putchar('\n'); + + return 0; } + +/************************************************************************ + * Now support for dynamical loading and the fallback functions + * we use for loading if dynamical loading is not available. + ************************************************************************/ + /**/ -int -bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) +#ifdef DYNAMIC + +/**/ +#ifdef AIXDYNAMIC + +#include + +static char *dlerrstr[256]; + +static void * +load_and_bind(const char *fn) { - 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; + void *ret = (void *) load((char *) fn, L_NOAUTODEFER, NULL); - 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,'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 (ret) { + LinkNode node; + int err = loadbind(0, (void *) addbuiltin, ret); + for (node = firstnode(modules); !err && node; incnode(node)) { + Module m = (Module) getdata(node); + if (!(m->flags & MOD_ALIAS) && + m->u.handle && !(m->flags & MOD_LINKED)) + err |= loadbind(0, m->u.handle, ret); } - if (!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,'d') || - OPT_ISSET(ops,'i') || OPT_ISSET(ops,'u'))) { - zwarnnam(nam, "-e cannot be combined with other options"); - return 1; - } - queue_signals(); - if (OPT_ISSET(ops,'e')) - ret = bin_zmodload_exist(nam, args, ops); - else if (OPT_ISSET(ops,'d')) - ret = bin_zmodload_dep(nam, args, ops); - else if ((OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b')) && - !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f'))) - ret = bin_zmodload_auto(nam, args, ops); - else if (OPT_ISSET(ops,'c') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p'))) - ret = bin_zmodload_cond(nam, args, ops); - else if (OPT_ISSET(ops,'f') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p'))) - ret = bin_zmodload_math(nam, args, ops); - else if (OPT_ISSET(ops,'p') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c'))) - ret = bin_zmodload_param(nam, args, ops); - else if (!(OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b') || - OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p'))) - ret = bin_zmodload_load(nam, args, ops); - else - zwarnnam(nam, "use only one of -b, -c, or -p"); - unqueue_signals(); + + 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]) + /**/ -static int -bin_zmodload_alias(char *nam, char **args, Options ops) +#else + +#ifdef HAVE_DLFCN_H +# if defined(HAVE_DL_H) && defined(HPUXDYNAMIC) +# include +# else +# include +# endif +#else +# ifdef HAVE_DL_H +# include +# define RTLD_LAZY BIND_DEFERRED +# define RTLD_GLOBAL DYNAMIC_PATH +# else +# include +# include +# include +# endif +#endif + +/**/ +#ifdef HPUXDYNAMIC +# define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +# define dlclose(handle) shl_unload((shl_t)(handle)) + +static +void * +hpux_dlsym(void *handle, char *name) { - /* - * TODO: while it would be too nasty to have aliases, as opposed - * to real loadable modules, with dependencies --- just what would - * we need to load when, exactly? --- there is in principle no objection - * to making it possible to force an alias onto an existing unloaded - * module which has dependencies. This would simply transfer - * the dependencies down the line to the aliased-to module name. - * This is actually useful, since then you can alias zsh/zle=mytestzle - * to load another version of zle. But then what happens when the - * alias is removed? Do you transfer the dependencies back? And - * suppose other names are aliased to the same file? It might be - * kettle of fish best left unwormed. - */ - LinkNode node; - Module m; + void *sym_addr; + if (!shl_findsym((shl_t *)&handle, name, TYPE_UNDEFINED, &sym_addr)) + return sym_addr; + return NULL; +} - if (!*args) { - if (OPT_ISSET(ops,'R')) { - zwarnnam(nam, "no module alias to remove"); - return 1; - } - for (node = firstnode(modules); node; incnode(node)) { - m = (Module) getdata(node); - if (m->flags & MOD_ALIAS) - printmodalias(m, ops); - } - return 0; +# define dlsym(handle,name) hpux_dlsym(handle,name) +# define dlerror() 0 +#else +# ifndef HAVE_DLCLOSE +# define dlclose(X) ((X), 0) +# endif +/**/ +#endif + +#ifdef DLSYM_NEEDS_UNDERSCORE +# define STR_SETUP "_setup_" +# define STR_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 + +/**/ +static void * +try_load_module(char const *name) +{ + char buf[PATH_MAX + 1]; + char **pp; + void *ret = NULL; + int l; + + l = 1 + strlen(name) + 1 + strlen(DL_EXT); + for (pp = module_path; !ret && *pp; pp++) { + if (l + (**pp ? strlen(*pp) : 1) > PATH_MAX) + continue; + sprintf(buf, "%s/%s.%s", **pp ? *pp : ".", name, DL_EXT); + ret = dlopen(unmeta(buf), RTLD_LAZY | RTLD_GLOBAL); } - 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; - } - node = find_module(*args, 0, NULL); - if (node) { - m = (Module) getdata(node); - if (!(m->flags & MOD_ALIAS)) { - zwarnnam(nam, "module is not an alias: %s", *args); - return 1; - } - delete_module(node); - } 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 ((node = find_module(mname, 0, NULL)) - && ((m = (Module) getdata(node))->flags & MOD_ALIAS) - && (mname = m->u.alias)); - node = find_module(*args, 0, NULL); - if (node) { - m = (Module) getdata(node); - if (!(m->flags & MOD_ALIAS)) { - zwarnnam(nam, "module is not an alias: %s", *args); - return 1; - } - zsfree(m->u.alias); - } else { - m = (Module) zshcalloc(sizeof(*m)); - m->nam = ztrdup(*args); - m->flags = MOD_ALIAS; - zaddlinknode(modules, m); - } - m->u.alias = ztrdup(aliasname); - } else { - if ((node = find_module(*args, 0, NULL))) { - m = (Module) getdata(node); - if (m->flags & MOD_ALIAS) - printmodalias(m, ops); - else { - zwarnnam(nam, "module is not an alias: %s", *args); - return 1; - } - } else { - zwarnnam(nam, "no such module alias: %s", *args); - return 1; - } + return ret; +} + +/**/ +static void * +do_load_module(char const *name, int silent) +{ + void *ret; + + ret = try_load_module(name); + if (!ret && !silent) + zwarn("failed to load module: %s", name); + return ret; +} + +/**/ +#else /* !DYNAMIC */ + +/**/ +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. + * If aliasp is non-zero, resolve any aliases to the underlying module. + * If namep is set, this is set to point to the last alias value resolved, + * even if that module was not loaded. or the module name if no aliases. + * Hence this is always the physical module to load in a chain of aliases. + * Return NULL if the module named is not stored as a structure, or if we were + * resolving aliases and the final module named is not stored as a + * structure. + * + * TODO: now we have aliases, there may be some merit in using a hash + * table instead of a linked list. + */ +/**/ +static LinkNode +find_module(const char *name, int aliasp, const char **namep) +{ + Module m; + LinkNode node; + + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (!strcmp(m->nam, name)) { + if (aliasp && (m->flags & MOD_ALIAS)) { + if (namep) + *namep = m->u.alias; + return find_module(m->u.alias, 1, namep); } + if (namep) + *namep = m->nam; + return node; } } + return NULL; +} - return 0; +/* + * Unlink and free a module node from the linked list. + */ + +/**/ +static void +delete_module(LinkNode node) +{ + Module m = (Module) remnode(modules, node); + + if (m->flags & MOD_ALIAS) + zsfree(m->u.alias); + zsfree(m->nam); + if (m->deps) + freelinklist(m->deps, freestr); + zfree(m, sizeof(*m)); } /**/ -static int -bin_zmodload_exist(UNUSED(char *nam), char **args, Options ops) +mod_export int +module_loaded(const char *name) { LinkNode node; Module m; - char *modname; - if (!*args) { - for (node = firstnode(modules); node; incnode(node)) { - m = (Module) getdata(node); - modname = m->nam; - if (m->flags & MOD_ALIAS) { - LinkNode node2; - if (OPT_ISSET(ops,'A') && - (node2 = find_module(m->u.alias, 1, NULL))) - m = (Module) getdata(node2); - else - continue; - } - if (m->u.handle && !(m->flags & MOD_UNLOAD)) { - nicezputs(modname, stdout); - putchar('\n'); - } - } - return 0; - } else { - int ret = 0; + return ((node = find_module(name, 1, NULL)) && + (m = ((Module) getdata(node)))->u.handle && + !(m->flags & MOD_UNLOAD)); +} - for (; !ret && *args; args++) { - if (!(node = find_module(*args, 1, NULL)) - || !(m = (Module) getdata(node))->u.handle - || (m->flags & MOD_UNLOAD)) - ret = 1; - } - return ret; - } +/* + * Setup and cleanup functions: we don't search for aliases here, + * since they should have been resolved before we try to load or unload + * the module. + */ + +/**/ +#ifdef DYNAMIC + +/**/ +#ifdef AIXDYNAMIC + +/**/ +static int +dyn_setup_module(Module m) +{ + return ((int (*)_((int,Module))) m->u.handle)(0, m, NULL); +} + +/**/ +static int +dyn_features_module(Module m, char ***features) +{ + return ((int (*)_((int,Module))) m->u.handle)(4, m, features); } /**/ static int -bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops) +dyn_enables_module(Module m, int **enables) { - LinkNode node; - Module m; - if (OPT_ISSET(ops,'u')) { - /* remove dependencies, which can't pertain to aliases */ - const char *tnam = *args++; - node = find_module(tnam, 1, &tnam); - if (!node) - return 0; - m = (Module) getdata(node); - if (*args && m->deps) { - do { - LinkNode dnode; - for (dnode = firstnode(m->deps); dnode; incnode(dnode)) - if (!strcmp(*args, getdata(dnode))) { - zsfree(getdata(dnode)); - remnode(m->deps, dnode); - break; - } - } while(*++args); - if (empty(m->deps)) { - freelinklist(m->deps, freestr); - m->deps = NULL; - } - } else { - if (m->deps) { - freelinklist(m->deps, freestr); - m->deps = NULL; - } - } - if (!m->deps && !m->u.handle) - delete_module(node); - return 0; - } else if (!args[0] || !args[1]) { - /* list dependencies */ - for (node = firstnode(modules); node; incnode(node)) { - m = (Module) getdata(node); - if (m->deps && (!args[0] || !strcmp(args[0], m->nam))) { - LinkNode n; - if (OPT_ISSET(ops,'L')) { - printf("zmodload -d "); - if(m->nam[0] == '-') - fputs("-- ", stdout); - quotedzputs(m->nam, stdout); - } else { - nicezputs(m->nam, stdout); - putchar(':'); - } - for (n = firstnode(m->deps); n; incnode(n)) { - putchar(' '); - if(OPT_ISSET(ops,'L')) - quotedzputs((char *) getdata(n), stdout); - else - nicezputs((char *) getdata(n), stdout); - } - putchar('\n'); - } - } - return 0; - } else { - /* add dependencies */ - int ret = 0; - char *tnam = *args++; - - for (; *args; args++) - add_dep(tnam, *args); - return ret; - } + return ((int (*)_((int,Module))) m->u.handle)(5, m, enables); } /**/ static int -bin_zmodload_auto(char *nam, char **args, Options ops) +dyn_boot_module(Module m) { - int ret = 0; - if(OPT_ISSET(ops,'u')) { - /* remove autoloaded builtins */ - for (; *args; args++) { - Builtin bn = (Builtin) builtintab->getnode2(builtintab, *args); - if (!bn) { - if(!OPT_ISSET(ops,'i')) { - zwarnnam(nam, "%s: no such builtin", *args); - ret = 1; - } - } else if (bn->node.flags & BINF_ADDED) { - zwarnnam(nam, "%s: builtin is already defined", *args); - ret = 1; - } else - deletebuiltin(*args); - } - return ret; - } else if(!*args) { - /* list autoloaded builtins */ - scanhashtable(builtintab, 1, 0, 0, - autoloadscan, OPT_ISSET(ops,'L') ? PRINT_LIST : 0); - return 0; - } else { - /* add autoloaded builtins */ - char *modnam; - modnam = *args++; - do { - char *bnam = *args ? *args++ : modnam; - if (strchr(bnam, '/')) { - zwarnnam(nam, "%s: `/' is illegal in a builtin", bnam); - ret = 1; - } else if (add_autobin(bnam, modnam) && !OPT_ISSET(ops,'i')) { - zwarnnam(nam, "failed to add builtin %s", bnam); - ret = 1; - } - } while(*args); - return ret; - } + return ((int (*)_((int,Module))) m->u.handle)(1, m, NULL); } /**/ static int -bin_zmodload_cond(char *nam, char **args, Options ops) +dyn_cleanup_module(Module m) { - int ret = 0; - - if (OPT_ISSET(ops,'u')) { - /* remove autoloaded conditions */ - for (; *args; args++) { - Conddef cd = getconddef(OPT_ISSET(ops,'I'), *args, 0); + return ((int (*)_((int,Module))) m->u.handle)(2, m, NULL); +} - if (!cd) { - if (!OPT_ISSET(ops,'i')) { - zwarnnam(nam, "%s: no such condition", *args); - ret = 1; - } - } else if (cd->flags & CONDF_ADDED) { - zwarnnam(nam, "%s: condition is already defined", *args); - ret = 1; - } else - deleteconddef(cd); - } - return ret; - } else if (!*args) { - /* list autoloaded conditions */ - Conddef p; +/**/ +static int +dyn_finish_module(Module m) +{ + return ((int (*)_((int,Module))) m->u.handle)(3, m, NULL); +} - for (p = condtab; p; p = p->next) { - if (p->module) { - if (OPT_ISSET(ops,'L')) { - fputs("zmodload -ac", stdout); - if (p->flags & CONDF_INFIX) - putchar('I'); - printf(" %s %s\n", p->module, p->name); - } else { - if (p->flags & CONDF_INFIX) - fputs("infix ", stdout); - else - fputs("post ", stdout); - printf("%s (%s)\n",p->name, p->module); - } - } - } - return 0; - } else { - /* add autoloaded conditions */ - char *modnam; +/**/ +#else - modnam = *args++; - do { - char *cnam = *args ? *args++ : modnam; - if (strchr(cnam, '/')) { - zwarnnam(nam, "%s: `/' is illegal in a condition", cnam); - ret = 1; - } else if (add_autocond(cnam, OPT_ISSET(ops,'I'), modnam) && - !OPT_ISSET(ops,'i')) { - zwarnnam(nam, "failed to add condition `%s'", cnam); - ret = 1; - } - } while(*args); - return ret; +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->nam)*2 + 1); + char const *p; + char *q; + strcpy(buf, name); + q = strchr(buf, 0); + for(p = m->nam; *p; p++) { + if(*p == '/') { + *q++ = 'Q'; + *q++ = 's'; + } else if(*p == '_') { + *q++ = 'Q'; + *q++ = 'u'; + } else if(*p == 'Q') { + *q++ = 'Q'; + *q++ = 'q'; + } else + *q++ = *p; } + *q = 0; + return (Module_generic_func) dlsym(m->u.handle, buf); +#endif /* !DYNAMIC_NAME_CLASH_OK */ } /**/ static int -bin_zmodload_math(char *nam, char **args, Options ops) +dyn_setup_module(Module m) { - int ret = 0; + Module_void_func fn = (Module_void_func)module_func(m, STR_SETUP); - if (OPT_ISSET(ops,'u')) { - /* remove autoloaded math functions */ - for (; *args; args++) { - MathFunc f = getmathfunc(*args, 0); + if (fn) + return fn(m); + zwarnnam(m->nam, "no setup function"); + return 1; +} - if (!f) { - if (!OPT_ISSET(ops,'i')) { - zwarnnam(nam, "%s: no such math function", *args); - ret = 1; - } - } else if (f->flags & MFF_ADDED) { - zwarnnam(nam, "%s: math function is already defined", *args); - ret = 1; - } else - deletemathfunc(f); - } - return ret; - } else if (!*args) { - /* list autoloaded math functions */ - MathFunc p; +/**/ +static int +dyn_features_module(Module m, char ***features) +{ + Module_features_func fn = + (Module_features_func)module_func(m, STR_FEATURES); - 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; - } else { - /* add autoloaded math functions */ - char *modnam; + if (fn) + return fn(m, features); + /* not a user-visible error if no features function */ + return 1; +} - modnam = *args++; - do { - char *fnam = *args ? *args++ : modnam; - if (strchr(fnam, '/')) { - zwarnnam(nam, "%s: `/' is illegal in a math function", fnam); - ret = 1; - } else if (add_automathfunc(fnam, modnam) && !OPT_ISSET(ops,'i')) { - zwarnnam(nam, "failed to add math function `%s'", fnam); - ret = 1; - } - } while(*args); - return ret; - } +/**/ +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 void -printautoparams(HashNode hn, int lon) +/**/ +static int +dyn_boot_module(Module m) { - Param pm = (Param) hn; + Module_void_func fn = (Module_void_func)module_func(m, STR_BOOT); - 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); - } + if(fn) + return fn(m); + zwarnnam(m->nam, "no boot function"); + return 1; } /**/ static int -bin_zmodload_param(char *nam, char **args, Options ops) +dyn_cleanup_module(Module m) { - int ret = 0; + Module_void_func fn = (Module_void_func)module_func(m, STR_CLEANUP); - if (OPT_ISSET(ops,'u')) { - /* remove autoloaded parameters */ - for (; *args; args++) { - Param pm = (Param) gethashnode2(paramtab, *args); + if(fn) + return fn(m); + zwarnnam(m->nam, "no cleanup function"); + return 1; +} - if (!pm) { - if (!OPT_ISSET(ops,'i')) { - zwarnnam(nam, "%s: no such parameter", *args); - ret = 1; - } - } else if (!(pm->node.flags & PM_AUTOLOAD)) { - zwarnnam(nam, "%s: parameter is already defined", *args); - ret = 1; - } else - unsetparam_pm(pm, 0, 1); - } - return ret; - } else if (!*args) { - scanhashtable(paramtab, 1, 0, 0, printautoparams, OPT_ISSET(ops,'L')); - return 0; - } else { - /* add autoloaded parameters */ - char *modnam; +/* Note that this function does more than just calling finish_foo(), * + * it really unloads the module. */ - modnam = *args++; - do { - char *pnam = *args ? *args++ : modnam; - if (strchr(pnam, '/')) { - zwarnnam(nam, "%s: `/' is illegal in a parameter", pnam); - ret = 1; - } else - add_autoparam(pnam, modnam); - } while(*args); - return ret; +/**/ +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->nam, "no finish function"); + r = 1; } + dlclose(m->u.handle); + return r; } /**/ -int -unload_module(Module m, LinkNode node) +#endif /* !AIXDYNAMIC */ + +/**/ +static int +setup_module(Module m) { - /* - * Only unload the real module, so resolve aliases. - */ - if (m->flags & MOD_ALIAS) { - LinkNode node = find_module(m->u.alias, 1, NULL); - if (!node) - return 1; - m = (Module) getdata(node); - } - if ((m->flags & MOD_INIT_S) && - !(m->flags & MOD_UNLOAD) && - ((m->flags & MOD_LINKED) ? - (m->u.linked && m->u.linked->cleanup(m)) : - (m->u.handle && cleanup_module(m)))) - return 1; - else { - int del = (m->flags & MOD_UNLOAD); + return ((m->flags & MOD_LINKED) ? + (m->u.linked->setup)(m) : dyn_setup_module(m)); +} - if (m->wrapper) { - m->flags |= MOD_UNLOAD; - return 0; - } - m->flags &= ~MOD_UNLOAD; - if (m->flags & MOD_INIT_B) { - if (m->flags & MOD_LINKED) { - if (m->u.linked) { - m->u.linked->finish(m); - m->u.linked = NULL; - } - } else { - if (m->u.handle) { - finish_module(m); - m->u.handle = NULL; - } - } - } - if (del && m->deps) { - /* The module was unloaded delayed, unload all modules * - * on which it depended. */ - LinkNode n; +/**/ +static int +features_module(Module m, char ***features) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->features)(m, features) : + dyn_features_module(m, features)); +} - for (n = firstnode(m->deps); n; incnode(n)) { - LinkNode dn = find_module((char *) getdata(n), 1, NULL); - Module dm; +/**/ +static int +enables_module(Module m, int **enables) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->enables)(m, enables) : + dyn_enables_module(m, enables)); +} - if (dn && (dm = (Module) getdata(dn)) && - (dm->flags & MOD_UNLOAD)) { - /* See if this is the only module depending on it. */ +/**/ +static int +boot_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->boot)(m) : dyn_boot_module(m)); +} - LinkNode an; - Module am; - int du = 1; +/**/ +static int +cleanup_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->cleanup)(m) : dyn_cleanup_module(m)); +} - for (an = firstnode(modules); du && an; incnode(an)) { - am = (Module) getdata(an); - if (am != m && am->deps && - ((am->flags & MOD_LINKED) ? - am->u.linked : am->u.handle)) { - LinkNode sn; +/**/ +static int +finish_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->finish)(m) : dyn_finish_module(m)); +} - for (sn = firstnode(am->deps); du && sn; - incnode(sn)) { - if (!strcmp((char *) getdata(sn), dm->nam)) - du = 0; - } - } - } - if (du) - unload_module(dm, NULL); - } - } - } - if(!m->deps) { - if (!node) { - for (node = firstnode(modules); node; incnode(node)) - if (m == (Module) getdata(node)) - break; - if (!node) - return 1; - } - delete_module(node); - } - } - return 0; +/**/ +#else /* !DYNAMIC */ + +/**/ +static int +setup_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? (m->u.linked->setup)(m) : 1); } +/**/ +static int +features_module(Module m, char ***features) +{ + return ((m->flags & MOD_LINKED) ? (m->u.linked->features)(m, features) + : 1); +} /**/ -int -unload_named_module(char *modname, char *nam, int silent) +static int +enables_module(Module m, int **enables) { - const char *mname; - LinkNode node; - Module m; - int ret = 0; + return ((m->flags & MOD_LINKED) ? (m->u.linked->enables)(m, enables) + : 1); +} - node = find_module(modname, 1, &mname); - if (node) { - LinkNode mn, dn; - int del = 0; +/**/ +static int +boot_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1); +} - for (mn = firstnode(modules); mn; incnode(mn)) { - m = (Module) getdata(mn); - if (m->deps && m->u.handle) - for (dn = firstnode(m->deps); dn; incnode(dn)) - if (!strcmp((char *) getdata(dn), mname)) { - if (m->flags & MOD_UNLOAD) - del = 1; - else { - zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", mname); - return 1; - } - } - } - m = (Module) getdata(node); - if (del) - m->wrapper++; - if (unload_module(m, node)) - ret = 1; - if (del) - m->wrapper--; - } else if (!silent) { - zwarnnam(nam, "no such module %s", modname); - ret = 1; - } +/**/ +static int +cleanup_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? (m->u.linked->cleanup)(m) : 1); +} - return ret; +/**/ +static int +finish_module(Module m) +{ + return ((m->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. + */ /**/ static int -bin_zmodload_load(char *nam, char **args, Options ops) +do_module_features(Module m, char **enablesstr, int silent) { - LinkNode node; - Module m; - 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; + char **features; + + 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 (!silent) + zwarn("error getting enabled features for module `%s'", + m->nam); + return 1; } - return ret; - } else if(!*args) { - /* list modules */ - for (node = firstnode(modules); node; incnode(node)) { - m = (Module) getdata(node); - if (m->u.handle && !(m->flags & (MOD_UNLOAD|MOD_ALIAS))) { - if(OPT_ISSET(ops,'L')) { - printf("zmodload "); - if(m->nam[0] == '-') - fputs("-- ", stdout); - quotedzputs(m->nam, stdout); - } else - nicezputs(m->nam, stdout); - putchar('\n'); + + if (enablesstr) { + char **ep; + for (ep = enablesstr; *ep; ep++) { + char **fp, *esp = *ep; + int on = 1; + if (*esp == '+') + esp++; + else if (*esp == '-') { + on = 0; + esp++; + } + for (fp = features; *fp; fp++) + if (!strcmp(*fp, esp)) { + enables[fp - features] = on; + break; + } + if (!*fp) { + if (!silent) + zwarn("module `%s' has no such feature: %s", + m->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; } - return 0; - } else { - /* load modules */ - for (; *args; args++) - if (!require_module(nam, *args, 1, (!OPT_ISSET(ops,'i')))) - ret = 1; - return ret; + if (enables_module(m, &enables)) + return 2; + } else if (enablesstr) { + if (!silent) + zwarn("module `%s' does not support features", m->nam); + return 1; } + /* Else it doesn't support features but we don't care. */ + + return 0; } -/* The list of module-defined conditions. */ +/* + * 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. + */ /**/ -mod_export Conddef condtab; +static int +do_boot_module(Module m, char **enablesstr, int silent) +{ + int ret = do_module_features(m, enablesstr, silent); -/* 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. */ + if (ret == 1) + return 1; + + if (boot_module(m)) + return 1; + return ret; +} + +/* + * Cleanup the module. + */ /**/ -Conddef -getconddef(int inf, char *name, int autol) +static int +do_cleanup_module(Module m) { - Conddef p; - int f = 1; + return (m->flags & MOD_LINKED) ? + (m->u.linked && m->u.linked->cleanup(m)) : + (m->u.handle && cleanup_module(m)); +} +/**/ +static int +modname_ok(char const *p) +{ do { - for (p = condtab; p; p = p->next) { - if ((!!inf == !!(p->flags & CONDF_INFIX)) && - !strcmp(name, p->name)) - break; - } - if (autol && p && p->module) { - /* This is a definition for an autoloaded condition, load the * - * module if we haven't tried that already. */ - if (f) { - load_module_silence(p->module, 0); - f = 0; - p = NULL; - } else { - deleteconddef(p); - return NULL; - } - } else - break; - } while (!p); - return p; + p = itype_end(p, IIDENT, 0); + if (!*p) + return 1; + } while(*p++ == '/'); + return 0; } -/* 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. */ +/* + * Now returns 0 for success (changed post-4.3.4), + * 1 for complete failure, 2 if some features couldn't be set. + */ /**/ -int -addconddef(Conddef c) +mod_export int +load_module(char const *name, char **enablesstr) { - 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; + return load_module_silence(name, enablesstr, 0); } -/* This adds multiple condition definitions. This is like addbuiltins(). */ +/* + * Returns 0 for success (changed post-4.3.4), 1 for complete + * failure, 2 if some features couldn't be set. + */ /**/ mod_export int -addconddefs(char const *nam, Conddef c, int size) +load_module_silence(char const *name, char **enablesstr, int silent) { - int hads = 0, hadf = 0; + Module m; + void *handle = NULL; + Linkedmod linked; + LinkNode node, n; + int set, bootret; - while (size--) { - if (c->flags & CONDF_ADDED) { - c++; - continue; + 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 (!(node = find_module(name, 1, &name))) { + if (!(linked = module_linked(name)) && + !(handle = do_load_module(name, silent))) { + unqueue_signals(); + return 1; } - if (addconddef(c)) { - zwarnnam(nam, "name clash when adding condition `%s'", c->name); - hadf = 1; + m = zshcalloc(sizeof(*m)); + m->nam = ztrdup(name); + if (handle) { + m->u.handle = handle; + m->flags |= MOD_SETUP; } else { - c->flags |= CONDF_ADDED; - hads = 2; + m->u.linked = linked; + m->flags |= MOD_SETUP | MOD_LINKED; } - c++; + node = zaddlinknode(modules, m); + + if ((set = setup_module(m)) || + (bootret = do_boot_module(m, enablesstr, silent)) == 1) { + if (!set) { + do_cleanup_module(m); + finish_module(m); + } + delete_module(node); + unqueue_signals(); + return 1; + } + m->flags |= MOD_INIT_S | MOD_INIT_B; + m->flags &= ~MOD_SETUP; + unqueue_signals(); + return bootret; } - return hadf ? hads : 1; + m = (Module) getdata(node); + if (m->flags & MOD_SETUP) { + unqueue_signals(); + return 0; + } + if (m->flags & MOD_UNLOAD) + m->flags &= ~MOD_UNLOAD; + else if ((m->flags & MOD_LINKED) ? m->u.linked : m->u.handle) { + unqueue_signals(); + return 0; + } + if (m->flags & MOD_BUSY) { + zerr("circular dependencies for module %s", name); + return 1; + } + m->flags |= MOD_BUSY; + /* + * TODO: shouldn't we unload the module if one of + * its dependencies fails? + */ + if (m->deps) + for (n = firstnode(m->deps); n; incnode(n)) + if (load_module_silence((char *) getdata(n), NULL, silent) == 1) { + m->flags &= ~MOD_BUSY; + unqueue_signals(); + return 1; + } + m->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->flags |= MOD_SETUP; + } else { + m->u.linked = linked; + m->flags |= MOD_SETUP | MOD_LINKED; + } + if (setup_module(m)) { + if (handle) + m->u.handle = NULL; + else + m->u.linked = NULL; + m->flags &= ~MOD_SETUP; + unqueue_signals(); + return 1; + } + m->flags |= MOD_INIT_S; + } + m->flags |= MOD_SETUP; + if ((bootret = do_boot_module(m, enablesstr, silent)) == 1) { + do_cleanup_module(m); + finish_module(m); + if (m->flags & MOD_LINKED) + m->u.linked = NULL; + else + m->u.handle = NULL; + m->flags &= ~MOD_SETUP; + unqueue_signals(); + return 1; + } + m->flags |= MOD_INIT_B; + m->flags &= ~MOD_SETUP; + unqueue_signals(); + return bootret; } -/* This list of hook functions defined. */ - -/**/ -Hookdef hooktab; - -/* Find a hook definition given the name. */ +/* This ensures that the module with the name given as the second argument + * is loaded. + * The last argument is the array of features to set. If this is NULL + * and the module needs to be loaded, all features are enabled. + * 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. + */ /**/ -Hookdef -gethookdef(char *n) +mod_export int +require_module(char *nam, const char *module, char **features) { - Hookdef p; + Module m = NULL; + LinkNode node; + int ret = 0; + + /* Resolve aliases and actual loadable module as for load_module */ + queue_signals(); + node = find_module(module, 1, &module); + if (!node || !(m = ((Module) getdata(node)))->u.handle || + (m->flags & MOD_UNLOAD)) + ret = load_module_silence(module, features, 0); + else + ret = do_module_features(m, features, 0); + unqueue_signals(); - for (p = hooktab; p; p = p->next) - if (!strcmp(n, p->name)) - return p; - return NULL; + return ret; } -/* This adds the given hook definition. The return value is zero on * - * success and 1 on failure. */ - /**/ -int -addhookdef(Hookdef h) +void +add_dep(const char *name, char *from) { - if (gethookdef(h->name)) - return 1; - - h->next = hooktab; - hooktab = h; - h->funcs = znewlinklist(); + LinkNode node; + Module m; - return 0; + /* + * If we were passed an alias, we must resolve it to a final + * module name (and maybe add the corresponding struct), since otherwise + * we would need to check all modules to see if they happen + * to be aliased to the same thing to implement dependencies properly. + * + * This should mean that an attempt to add an alias which would + * have the same name as a module which has dependencies is correctly + * rejected, because then the module named already exists as a non-alias. + * Better make sure. (There's no problem making a an alias which + * *points* to a module with dependencies, of course.) + */ + if (!(node = find_module(name, 1, &name))) { + m = zshcalloc(sizeof(*m)); + m->nam = ztrdup(name); + zaddlinknode(modules, m); + } else + m = (Module) getdata(node); + if (!m->deps) + m->deps = znewlinklist(); + for (node = firstnode(m->deps); + node && strcmp((char *) getdata(node), from); + incnode(node)); + if (!node) + zaddlinknode(m->deps, ztrdup(from)); } -/* This adds multiple hook definitions. This is like addbuiltins(). */ - /**/ -mod_export int -addhookdefs(char const *nam, Hookdef h, int size) +static void +autoloadscan(HashNode hn, int printflags) { - int hads = 0, hadf = 0; + Builtin bn = (Builtin) hn; - while (size--) { - if (addhookdef(h)) { - zwarnnam(nam, "name clash when adding hook `%s'", h->name); - hadf = 1; - } else - hads = 2; - h++; + 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(')'); + } } - return hadf ? hads : 1; + putchar('\n'); } -/* Delete hook definitions. */ + +/************************************************************************ + * Handling for the zmodload builtin and its various options. + ************************************************************************/ /**/ int -deletehookdef(Hookdef h) +bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) { - Hookdef p, q; - - for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next); + 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; + /* options only allowed with -F */ + char *fonly = "lP", *fp; - if (!p) + if (ops_bcpf) { + if (!ops_au) { + zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u"); + return 1; + } + if (OPT_ISSET(ops,'F')) { + zwarnnam(nam, "-b, -c, -f, and -p 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 (q) - q->next = p->next; + } + 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,'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 ((OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b')) && + !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f'))) + ret = bin_zmodload_auto(nam, args, ops); + else if (OPT_ISSET(ops,'c') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p'))) + ret = bin_zmodload_cond(nam, args, ops); + else if (OPT_ISSET(ops,'f') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p'))) + ret = bin_zmodload_math(nam, args, ops); + else if (OPT_ISSET(ops,'p') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c'))) + ret = bin_zmodload_param(nam, args, ops); + else if (!(OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b') || + OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p'))) + ret = bin_zmodload_load(nam, args, ops); else - hooktab = p->next; - freelinklist(p->funcs, NULL); - return 0; + zwarnnam(nam, "use only one of -b, -c, or -p"); + unqueue_signals(); + + return ret; } /**/ -mod_export int -deletehookdefs(UNUSED(char const *nam), Hookdef h, int size) +static int +bin_zmodload_alias(char *nam, char **args, Options ops) { - while (size--) { - deletehookdef(h); - h++; + /* + * TODO: while it would be too nasty to have aliases, as opposed + * to real loadable modules, with dependencies --- just what would + * we need to load when, exactly? --- there is in principle no objection + * to making it possible to force an alias onto an existing unloaded + * module which has dependencies. This would simply transfer + * the dependencies down the line to the aliased-to module name. + * This is actually useful, since then you can alias zsh/zle=mytestzle + * to load another version of zle. But then what happens when the + * alias is removed? Do you transfer the dependencies back? And + * suppose other names are aliased to the same file? It might be + * kettle of fish best left unwormed. + */ + LinkNode node; + Module m; + + if (!*args) { + if (OPT_ISSET(ops,'R')) { + zwarnnam(nam, "no module alias to remove"); + return 1; + } + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (m->flags & MOD_ALIAS) + printmodalias(m, ops); + } + 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; + } + node = find_module(*args, 0, NULL); + if (node) { + m = (Module) getdata(node); + if (!(m->flags & MOD_ALIAS)) { + zwarnnam(nam, "module is not an alias: %s", *args); + return 1; + } + delete_module(node); + } 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 ((node = find_module(mname, 0, NULL)) + && ((m = (Module) getdata(node))->flags & MOD_ALIAS) + && (mname = m->u.alias)); + node = find_module(*args, 0, NULL); + if (node) { + m = (Module) getdata(node); + if (!(m->flags & MOD_ALIAS)) { + zwarnnam(nam, "module is not an alias: %s", *args); + return 1; + } + zsfree(m->u.alias); + } else { + m = (Module) zshcalloc(sizeof(*m)); + m->nam = ztrdup(*args); + m->flags = MOD_ALIAS; + zaddlinknode(modules, m); + } + m->u.alias = ztrdup(aliasname); + } else { + if ((node = find_module(*args, 0, NULL))) { + m = (Module) getdata(node); + if (m->flags & MOD_ALIAS) + printmodalias(m, ops); + else { + zwarnnam(nam, "module is not an alias: %s", *args); + return 1; + } + } else { + zwarnnam(nam, "no such module alias: %s", *args); + return 1; + } + } + } } - return 1; -} - -/* Add a function to a hook. */ - -/**/ -int -addhookdeffunc(Hookdef h, Hookfn f) -{ - zaddlinknode(h->funcs, (void *) f); return 0; } /**/ -mod_export int -addhookfunc(char *n, Hookfn f) +static int +bin_zmodload_exist(UNUSED(char *nam), char **args, Options ops) { - Hookdef h = gethookdef(n); - - if (h) - return addhookdeffunc(h, f); - return 1; -} - -/* Delete a function from a hook. */ + LinkNode node; + Module m; + char *modname; -/**/ -int -deletehookdeffunc(Hookdef h, Hookfn f) -{ - LinkNode p; + if (!*args) { + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + modname = m->nam; + if (m->flags & MOD_ALIAS) { + LinkNode node2; + if (OPT_ISSET(ops,'A') && + (node2 = find_module(m->u.alias, 1, NULL))) + m = (Module) getdata(node2); + else + continue; + } + if (m->u.handle && !(m->flags & MOD_UNLOAD)) { + nicezputs(modname, stdout); + putchar('\n'); + } + } + return 0; + } else { + int ret = 0; - for (p = firstnode(h->funcs); p; incnode(p)) - if (f == (Hookfn) getdata(p)) { - remnode(h->funcs, p); - return 0; + for (; !ret && *args; args++) { + if (!(node = find_module(*args, 1, NULL)) + || !(m = (Module) getdata(node))->u.handle + || (m->flags & MOD_UNLOAD)) + ret = 1; } - return 1; + return ret; + } } /**/ -mod_export int -deletehookfunc(char *n, Hookfn f) +static int +bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops) { - Hookdef h = gethookdef(n); + LinkNode node; + Module m; + if (OPT_ISSET(ops,'u')) { + /* remove dependencies, which can't pertain to aliases */ + const char *tnam = *args++; + node = find_module(tnam, 1, &tnam); + if (!node) + return 0; + m = (Module) getdata(node); + if (*args && m->deps) { + do { + LinkNode dnode; + for (dnode = firstnode(m->deps); dnode; incnode(dnode)) + if (!strcmp(*args, getdata(dnode))) { + zsfree(getdata(dnode)); + remnode(m->deps, dnode); + break; + } + } while(*++args); + if (empty(m->deps)) { + freelinklist(m->deps, freestr); + m->deps = NULL; + } + } else { + if (m->deps) { + freelinklist(m->deps, freestr); + m->deps = NULL; + } + } + if (!m->deps && !m->u.handle) + delete_module(node); + return 0; + } else if (!args[0] || !args[1]) { + /* list dependencies */ + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (m->deps && (!args[0] || !strcmp(args[0], m->nam))) { + LinkNode n; + if (OPT_ISSET(ops,'L')) { + printf("zmodload -d "); + if(m->nam[0] == '-') + fputs("-- ", stdout); + quotedzputs(m->nam, stdout); + } else { + nicezputs(m->nam, stdout); + putchar(':'); + } + for (n = firstnode(m->deps); n; incnode(n)) { + putchar(' '); + if(OPT_ISSET(ops,'L')) + quotedzputs((char *) getdata(n), stdout); + else + nicezputs((char *) getdata(n), stdout); + } + putchar('\n'); + } + } + return 0; + } else { + /* add dependencies */ + int ret = 0; + char *tnam = *args++; - if (h) - return deletehookdeffunc(h, f); - return 1; + for (; *args; args++) + add_dep(tnam, *args); + return ret; + } } -/* Run the function(s) for a hook. */ - /**/ -mod_export int -runhookdef(Hookdef h, void *d) +static int +bin_zmodload_auto(char *nam, char **args, Options ops) { - 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); + int ret = 0; + if(OPT_ISSET(ops,'u')) { + /* remove autoloaded builtins */ + for (; *args; args++) { + Builtin bn = (Builtin) builtintab->getnode2(builtintab, *args); + if (!bn) { + if(!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "%s: no such builtin", *args); + ret = 1; + } + } else if (bn->node.flags & BINF_ADDED) { + zwarnnam(nam, "%s: builtin is already defined", *args); + ret = 1; + } else + deletebuiltin(*args); + } + return ret; + } else if(!*args) { + /* list autoloaded builtins */ + scanhashtable(builtintab, 1, 0, 0, + autoloadscan, OPT_ISSET(ops,'L') ? PRINT_LIST : 0); return 0; - } else - return ((Hookfn) getdata(lastnode(h->funcs)))(h, d); + } else { + /* add autoloaded builtins */ + char *modnam; + modnam = *args++; + do { + char *bnam = *args ? *args++ : modnam; + if (strchr(bnam, '/')) { + zwarnnam(nam, "%s: `/' is illegal in a builtin", bnam); + ret = 1; + } else if (add_autobin(bnam, modnam) && !OPT_ISSET(ops,'i')) { + zwarnnam(nam, "failed to add builtin %s", bnam); + ret = 1; + } + } while(*args); + return ret; + } } /**/ -int -runhook(char *n, void *d) +static int +bin_zmodload_cond(char *nam, char **args, Options ops) { - Hookdef h = gethookdef(n); + int ret = 0; - if (h) - return runhookdef(h, d); - return 0; + if (OPT_ISSET(ops,'u')) { + /* remove autoloaded conditions */ + for (; *args; args++) { + Conddef cd = getconddef(OPT_ISSET(ops,'I'), *args, 0); + + if (!cd) { + if (!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "%s: no such condition", *args); + ret = 1; + } + } else if (cd->flags & CONDF_ADDED) { + zwarnnam(nam, "%s: condition is already defined", *args); + ret = 1; + } else + deleteconddef(cd); + } + return ret; + } else if (!*args) { + /* list autoloaded conditions */ + Conddef p; + + for (p = condtab; p; p = p->next) { + if (p->module) { + if (OPT_ISSET(ops,'L')) { + fputs("zmodload -ac", stdout); + if (p->flags & CONDF_INFIX) + putchar('I'); + printf(" %s %s\n", p->module, p->name); + } else { + if (p->flags & CONDF_INFIX) + fputs("infix ", stdout); + else + fputs("post ", stdout); + printf("%s (%s)\n",p->name, p->module); + } + } + } + return 0; + } else { + /* add autoloaded conditions */ + char *modnam; + + modnam = *args++; + do { + char *cnam = *args ? *args++ : modnam; + if (strchr(cnam, '/')) { + zwarnnam(nam, "%s: `/' is illegal in a condition", cnam); + ret = 1; + } else if (add_autocond(cnam, OPT_ISSET(ops,'I'), modnam) && + !OPT_ISSET(ops,'i')) { + zwarnnam(nam, "failed to add condition `%s'", cnam); + ret = 1; + } + } while(*args); + return ret; + } } -/* This adds the given parameter definition. The return value is zero on * - * success and 1 on failure. */ - /**/ -int -addparamdef(Paramdef d) +static int +bin_zmodload_math(char *nam, char **args, Options ops) { - Param pm; - - if ((pm = (Param) gethashnode2(paramtab, d->name))) - unsetparam_pm(pm, 0, 1); - - if (!(pm = createparam(d->name, d->flags)) && - !(pm = (Param) paramtab->getnode(paramtab, d->name))) - return 1; - - pm->level = 0; - pm->u.data = d->var; - if (d->gsu) - pm->gsu.i = (GsuInteger) d->gsu; - else { - /* - * If no get/set/unset class, use the appropriate - * variable type. - */ - switch (PM_TYPE(pm->node.flags)) { - case PM_SCALAR: - pm->gsu.s = &varscalar_gsu; - break; - - case PM_INTEGER: - pm->gsu.i = &varinteger_gsu; - break; + int ret = 0; - case PM_ARRAY: - pm->gsu.a = &vararray_gsu; - break; + if (OPT_ISSET(ops,'u')) { + /* remove autoloaded math functions */ + for (; *args; args++) { + MathFunc f = getmathfunc(*args, 0); - default: - unsetparam_pm(pm, 0, 1); - return 1; + if (!f) { + if (!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "%s: no such math function", *args); + ret = 1; + } + } else if (f->flags & MFF_ADDED) { + zwarnnam(nam, "%s: math function is already defined", *args); + ret = 1; + } else + deletemathfunc(f); } - } - - return 0; -} - -/* This adds multiple parameter definitions. This is like addbuiltins(). */ + return ret; + } else if (!*args) { + /* list autoloaded math functions */ + MathFunc p; -/**/ -mod_export int -addparamdefs(char const *nam, Paramdef d, int size) -{ - int hads = 0, hadf = 0; + 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; + } else { + /* add autoloaded math functions */ + char *modnam; - while (size--) { - if (addparamdef(d)) { - zwarnnam(nam, "error when adding parameter `%s'", d->name); - hadf = 1; - } else - hads = 2; - d++; + modnam = *args++; + do { + char *fnam = *args ? *args++ : modnam; + if (strchr(fnam, '/')) { + zwarnnam(nam, "%s: `/' is illegal in a math function", fnam); + ret = 1; + } else if (add_automathfunc(fnam, modnam) && !OPT_ISSET(ops,'i')) { + zwarnnam(nam, "failed to add math function `%s'", fnam); + ret = 1; + } + } while(*args); + return ret; } - return hadf ? hads : 1; } -/* Delete parameters defined. No error checking yet. */ - -/**/ -int -deleteparamdef(Paramdef d) +static void +printautoparams(HashNode hn, int lon) { - unsetparam(d->name); - return 0; -} + Param pm = (Param) hn; -/**/ -mod_export int -deleteparamdefs(UNUSED(char const *nam), Paramdef d, int size) -{ - while (size--) { - deleteparamdef(d); - d++; + 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); } - return 1; } -/* This adds a definition for autoloading a module for a condition. */ - /**/ -int -add_autocond(char *nam, int inf, char *module) +static int +bin_zmodload_param(char *nam, char **args, Options ops) { - Conddef c = (Conddef) zalloc(sizeof(*c)); + int ret = 0; - c->name = ztrdup(nam); - c->flags = (inf ? CONDF_INFIX : 0); - c->module = ztrdup(module); + if (OPT_ISSET(ops,'u')) { + /* remove autoloaded parameters */ + for (; *args; args++) { + Param pm = (Param) gethashnode2(paramtab, *args); - if (addconddef(c)) { - zsfree(c->name); - zsfree(c->module); - zfree(c, sizeof(*c)); + if (!pm) { + if (!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "%s: no such parameter", *args); + ret = 1; + } + } else if (!(pm->node.flags & PM_AUTOLOAD)) { + zwarnnam(nam, "%s: parameter is already defined", *args); + ret = 1; + } else + unsetparam_pm(pm, 0, 1); + } + return ret; + } else if (!*args) { + scanhashtable(paramtab, 1, 0, 0, printautoparams, OPT_ISSET(ops,'L')); + return 0; + } else { + /* add autoloaded parameters */ + char *modnam; - return 1; + modnam = *args++; + do { + char *pnam = *args ? *args++ : modnam; + if (strchr(pnam, '/')) { + zwarnnam(nam, "%s: `/' is illegal in a parameter", pnam); + ret = 1; + } else + add_autoparam(pnam, modnam); + } while(*args); + return ret; } - 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) +unload_module(Module m, LinkNode node) { - Conddef p, q; - - for (p = condtab, q = NULL; p && p != c; q = p, p = p->next); + /* + * Only unload the real module, so resolve aliases. + */ + if (m->flags & MOD_ALIAS) { + LinkNode node = find_module(m->u.alias, 1, NULL); + if (!node) + return 1; + m = (Module) getdata(node); + } + if ((m->flags & MOD_INIT_S) && + !(m->flags & MOD_UNLOAD) && + do_cleanup_module(m)) + return 1; + else { + int del = (m->flags & MOD_UNLOAD); - 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)); + if (m->wrapper) { + m->flags |= MOD_UNLOAD; + return 0; } - return 0; - } - return -1; -} + m->flags &= ~MOD_UNLOAD; + if (m->flags & MOD_INIT_B) { + if (m->flags & MOD_LINKED) { + if (m->u.linked) { + m->u.linked->finish(m); + m->u.linked = NULL; + } + } else { + if (m->u.handle) { + finish_module(m); + m->u.handle = NULL; + } + } + } + if (del && m->deps) { + /* The module was unloaded delayed, unload all modules * + * on which it depended. */ + LinkNode n; + + for (n = firstnode(m->deps); n; incnode(n)) { + LinkNode dn = find_module((char *) getdata(n), 1, NULL); + Module dm; -/* This removes multiple condition definitions (like deletebuiltins()). */ + if (dn && (dm = (Module) getdata(dn)) && + (dm->flags & MOD_UNLOAD)) { + /* See if this is the only module depending on it. */ -/**/ -mod_export int -deleteconddefs(char const *nam, Conddef c, int size) -{ - int hads = 0, hadf = 0; + LinkNode an; + Module am; + int du = 1; - while (size--) { - if (!(c->flags & CONDF_ADDED)) { - c++; - continue; + for (an = firstnode(modules); du && an; incnode(an)) { + am = (Module) getdata(an); + if (am != m && am->deps && + ((am->flags & MOD_LINKED) ? + am->u.linked : am->u.handle)) { + LinkNode sn; + + for (sn = firstnode(am->deps); du && sn; + incnode(sn)) { + if (!strcmp((char *) getdata(sn), dm->nam)) + du = 0; + } + } + } + if (du) + unload_module(dm, NULL); + } + } + } + if(!m->deps) { + if (!node) { + for (node = firstnode(modules); node; incnode(node)) + if (m == (Module) getdata(node)) + break; + if (!node) + return 1; + } + delete_module(node); } - if (deleteconddef(c)) { - zwarnnam(nam, "condition `%s' already deleted", c->name); - hadf = 1; - } else - hads = 2; - c->flags &= ~CONDF_ADDED; - c++; } - return hadf ? hads : 1; + return 0; } -/* This adds a definition for autoloading a module for a parameter. */ /**/ -void -add_autoparam(char *nam, char *module) +int +unload_named_module(char *modname, char *nam, int silent) { - Param pm; + const char *mname; + LinkNode node; + Module m; + int ret = 0; - queue_signals(); - if ((pm = (Param) gethashnode2(paramtab, nam))) - unsetparam_pm(pm, 0, 1); + node = find_module(modname, 1, &mname); + if (node) { + LinkNode mn, dn; + int del = 0; - pm = setsparam(nam, ztrdup(module)); + for (mn = firstnode(modules); mn; incnode(mn)) { + m = (Module) getdata(mn); + if (m->deps && m->u.handle) + for (dn = firstnode(m->deps); dn; incnode(dn)) + if (!strcmp((char *) getdata(dn), mname)) { + if (m->flags & MOD_UNLOAD) + del = 1; + else { + zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", mname); + return 1; + } + } + } + m = (Module) getdata(node); + if (del) + m->wrapper++; + if (unload_module(m, node)) + ret = 1; + if (del) + m->wrapper--; + } else if (!silent) { + zwarnnam(nam, "no such module %s", modname); + ret = 1; + } - pm->node.flags |= PM_AUTOLOAD; - unqueue_signals(); + return ret; } -/* List of math functions. */ - -/**/ -MathFunc mathfuncs; - -/**/ -void -removemathfunc(MathFunc previous, MathFunc current) -{ - if (previous) - previous->next = current->next; - else - mathfuncs = current->next; - - zsfree(current->name); - zsfree(current->module); - zfree(current, sizeof(*current)); -} /**/ -MathFunc -getmathfunc(char *name, int autol) +static int +bin_zmodload_load(char *nam, char **args, Options ops) { - 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); - - removemathfunc(q, p); - - load_module_silence(n, 0); - - return getmathfunc(name, 0); + LinkNode node; + Module m; + 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 */ + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (m->u.handle && !(m->flags & (MOD_UNLOAD|MOD_ALIAS))) { + if(OPT_ISSET(ops,'L')) { + printf("zmodload "); + if(m->nam[0] == '-') + fputs("-- ", stdout); + quotedzputs(m->nam, stdout); + } else + nicezputs(m->nam, stdout); + putchar('\n'); } - return p; + } + return 0; + } else { + /* load modules */ + for (; *args; args++) { + int tmpret = require_module(nam, *args, NULL); + if (tmpret && ret != 1) + ret = tmpret; } - return NULL; + return ret; + } } /**/ -mod_export int -addmathfunc(MathFunc f) +static int +bin_zmodload_features(char *nam, char **args, Options ops) { - MathFunc p, q = NULL; + char *modname = *args; - if (f->flags & MFF_ADDED) + if (!modname) { + zwarnnam(nam, "-F requires a module name"); return 1; + } + args++; - 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; - } + 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. + */ + LinkNode node; + Module m = NULL; + char **features, **fp, **arrset = NULL, **arrp = NULL; + int *enables = NULL, *ep; + char *param = OPT_ARG_SAFE(ops,'P'); + + node = find_module(modname, 1, NULL); + if (node) + m = ((Module) getdata(node)); + if (!m || !m->u.handle || (m->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->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->nam); + return 1; + } + for (arrp = args; *arrp; arrp++) { + char *arg = *arrp; + int on; + if (*arg == '-') { + on = 0; + arg++; + } else if (*arg == '+') { + on = 1; + arg++; + } else + on = -1; + for (fp = features, ep = enables; *fp; fp++, ep++) { + if (!strcmp(arg, *fp)) { + /* for -e, check given state, if any */ + if (OPT_ISSET(ops,'e') && on != -1 && + on != (*ep & 1)) + return 1; + break; + } + } + if (!*fp) { + if (!OPT_ISSET(ops,'e')) + zwarnnam(nam, "module `%s' has no such feature: %s", + *arrp); + return 1; + } + } + if (OPT_ISSET(ops,'e')) /* yep, everything we want exists */ + return 0; + if (OPT_ISSET(ops,'P')) { + 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; *argp; argp++) { + char *arg = *argp; + /* ignore +/- for consistency */ + if (*arg == '+' || *arg == '-') + arg++; + if (!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->nam); + for (fp = features, ep = enables; *fp; fp++, ep++) { + char *onoff; + int term; + if (*args) { + char **argp; + for (argp = args; *argp; argp++) { + char *arg = *argp; + if (*arg == '+' || *arg == '-') + arg++; + if (!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; + } - f->flags |= MFF_ADDED; - f->next = mathfuncs; - mathfuncs = f; - - return 0; + return require_module(nam, modname, args); } + +/************************************************************************ + * 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 int -addmathfuncs(char const *nam, MathFunc f, int size) +mod_export char ** +featuresarray(char const *nam, Features f) { - int hads = 0, hadf = 0; + int bn_size = f->bn_size, cd_size = f->cd_size; + int pd_size = f->pd_size, mf_size = f->mf_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; + Paramdef pdp = f->pd_list; + MathFunc mfp = f->mf_list; + char **features = (char **)zhalloc((features_size + 1) * sizeof(char *)); + char **featurep = features; - while (size--) { - if (f->flags & MFF_ADDED) { - f++; - continue; - } - if (addmathfunc(f)) { - zwarnnam(nam, "name clash when adding math function `%s'", - f->name); - hadf = 1; - } else - hads = 2; - f++; - } - return hadf ? hads : 1; + while (bn_size--) + *featurep++ = dyncat("b:", (bnp++)->node.nam); + while (cd_size--) + *featurep++ = dyncat("c:", (cdp++)->name); + while (pd_size--) + *featurep++ = dyncat("p:", (pdp++)->name); + while (mf_size--) + *featurep++ = dyncat("f:", (mfp++)->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. + */ /**/ -int -add_automathfunc(char *nam, char *module) +mod_export int * +getfeatureenables(char const *nam, Features f) { - MathFunc f = (MathFunc) zalloc(sizeof(*f)); - - f->name = ztrdup(nam); - f->module = ztrdup(module); - f->flags = 0; - - if (addmathfunc(f)) { - zsfree(f->name); - zsfree(f->module); - zfree(f, sizeof(*f)); + int bn_size = f->bn_size, cd_size = f->cd_size; + int pd_size = f->pd_size, mf_size = f->mf_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; + Paramdef pdp = f->pd_list; + MathFunc mfp = f->mf_list; + int *enables = zhalloc(sizeof(int) * features_size); + int *enablep = enables; - return 1; - } - f->flags &= ~MFF_ADDED; /* still to autoload, not added yet */ + while (bn_size--) + *enablep++ = ((bnp++)->node.flags & BINF_ADDED) ? 1 : 0; + while (cd_size--) + *enablep++ = ((cdp++)->flags & CONDF_ADDED) ? 1 : 0; + while (pd_size--) + *enablep++ = (pdp++)->pm ? 1 : 0; + while (mf_size--) + *enablep++ = ((mfp++)->flags & MFF_ADDED) ? 1 : 0; - return 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 -deletemathfunc(MathFunc f) +setfeatureenables(char const *nam, Features f, int *e) { - 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; + int ret = 0; - return 0; - } - return -1; + if (f->bn_size && setbuiltins(nam, f->bn_list, f->bn_size, e) != 1) + ret = 1; + if (e) + e += f->bn_size; + if (f->cd_size && setconddefs(nam, f->cd_list, f->cd_size, e) != 1) + ret = 1; + if (e) + e += f->cd_size; + if (f->pd_size && setparamdefs(nam, f->pd_list, f->pd_size, e) != 1) + ret = 1; + if (e) + e += f->pd_size; + if (f->mf_size && setmathfuncs(nam, f->mf_list, f->mf_size, e) != 1) + ret = 1; + return ret; } - + /**/ mod_export int -deletemathfuncs(char const *nam, MathFunc f, int size) +handlefeatures(char *nam, Features f, int **enables) { - int hads = 0, hadf = 0; - - while (size--) { - if (!(f->flags & MFF_ADDED)) { - f++; - continue; - } - if (deletemathfunc(f)) { - zwarnnam(nam, "math function `%s' already deleted", f->name); - hadf = 1; - } else - hads = 2; - f++; - } - return hadf ? hads : 1; + if (!enables || *enables) + return setfeatureenables(nam, f, *enables); + *enables = getfeatureenables(nam, f); + return 0; } -- cgit 1.4.1