diff options
Diffstat (limited to 'Src/module.c')
-rw-r--r-- | Src/module.c | 1983 |
1 files changed, 1308 insertions, 675 deletions
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 * @@ -49,6 +63,24 @@ setup_(UNUSED(Module m)) /**/ int +features_(UNUSED(Module m), UNUSED(char ***features)) +{ + /* + * There are lots and lots of features, but they're not + * handled here. + */ + return 1; +} + +/**/ +int +enables_(UNUSED(Module m), UNUSED(int **enables)) +{ + return 1; +} + +/**/ +int boot_(UNUSED(Module m)) { return 0; @@ -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,104 +330,713 @@ addwrapper(Module m, FuncWrap w) return 0; } -/* $module_path ($MODULE_PATH) */ +/* This removes the given wrapper definition from the list. Returned is * + * one in case of error and zero otherwise. */ /**/ -char **module_path; +mod_export int +deletewrapper(Module m, FuncWrap w) +{ + FuncWrap p, q; -/* List of modules */ + if (m->flags & MOD_ALIAS) + return 1; + + if (w->flags & WRAPF_ADDED) { + for (p = wrappers, q = NULL; p && p != w; q = p, p = p->next); + + if (p) { + if (q) + q->next = p->next; + else + wrappers = p->next; + p->flags &= ~WRAPF_ADDED; + + return 0; + } + } + return 1; +} + + +/************************************************************************ + * Conditions. + ************************************************************************/ + +/* The list of module-defined conditions. */ /**/ -mod_export LinkList modules; +mod_export Conddef condtab; -/* 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. */ +/* This gets a condition definition with the given name. The first * + * argument says if we have to look for an infix condition. The last * + * argument is non-zero if we should autoload modules if needed. */ + +/**/ +Conddef +getconddef(int inf, char *name, int autol) +{ + Conddef p; + int f = 1; + + do { + for (p = condtab; p; p = p->next) { + if ((!!inf == !!(p->flags & CONDF_INFIX)) && + !strcmp(name, p->name)) + break; + } + if (autol && p && p->module) { + /* This is a definition for an autoloaded condition, load the * + * module if we haven't tried that already. */ + if (f) { + (void)load_module_silence(p->module, NULL, 0); + f = 0; + p = NULL; + } else { + deleteconddef(p); + return NULL; + } + } else + break; + } while (!p); + return p; +} + +/* + * This adds the given condition definition. The return value is zero on * + * success and 1 on failure. If there is a matching definition for an * + * autoloaded condition, it is removed. + * + * This is used for adding both an autoload definition or + * a real condition. In the latter case the caller is responsible + * for setting the CONDF_ADDED flag. + */ + +/**/ +static int +addconddef(Conddef c) +{ + Conddef p = getconddef((c->flags & CONDF_INFIX), c->name, 0); + + if (p) { + if (!p->module || (p->flags & CONDF_ADDED)) + return 1; + /* There is an autoload definition. */ + + deleteconddef(p); + } + c->next = condtab; + condtab = c; + return 0; +} + +/* This removes the given condition definition from the list(s). If this * + * is a definition for a autoloaded condition, the memory is freed. */ /**/ int -add_autobin(char *nam, char *module) +deleteconddef(Conddef c) { - Builtin bn = zshcalloc(sizeof(*bn)); - bn->node.nam = ztrdup(nam); - bn->optstr = ztrdup(module); - return addbuiltin(bn); + Conddef p, q; + + for (p = condtab, q = NULL; p && p != c; q = p, p = p->next); + + if (p) { + if (q) + q->next = p->next; + else + condtab = p->next; + + if (p->module) { + /* autoloaded, free it */ + zsfree(p->name); + zsfree(p->module); + zfree(p, sizeof(*p)); + } + return 0; + } + return -1; } -/* Remove the builtin added previously by addbuiltin(). Returns * - * zero on succes and -1 if there is no builtin with that name. */ +/**/ +mod_export int +setconddefs(char const *nam, Conddef c, int size, int *e) +{ + int hads = 0, hadf = 0; + + while (size--) { + if (e && *e++) { + if (c->flags & CONDF_ADDED) { + c++; + continue; + } + if (addconddef(c)) { + zwarnnam(nam, "name clash when adding condition `%s'", + c->name); + 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 hadf ? hads : 1; +} + +/* This adds a definition for autoloading a module for a condition. */ /**/ int -deletebuiltin(char *nam) +add_autocond(char *nam, int inf, char *module) { - Builtin bn; + Conddef c = (Conddef) zalloc(sizeof(*c)); - bn = (Builtin) builtintab->removenode(builtintab, nam); - if (!bn) - return -1; - builtintab->freenode(&bn->node); + 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; } -/* 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. */ + +/************************************************************************ + * Hook functions. + ************************************************************************/ + +/* This list of hook functions defined. */ + +/**/ +Hookdef hooktab; + +/* Find a hook definition given the name. */ + +/**/ +Hookdef +gethookdef(char *n) +{ + Hookdef p; + + for (p = hooktab; p; p = p->next) + if (!strcmp(n, p->name)) + return p; + return NULL; +} + +/* This adds the given hook definition. The return value is zero on * + * success and 1 on failure. */ + +/**/ +int +addhookdef(Hookdef h) +{ + if (gethookdef(h->name)) + return 1; + + h->next = hooktab; + hooktab = h; + h->funcs = znewlinklist(); + + return 0; +} + +/* This adds multiple hook definitions. This is like addbuiltins(). */ /**/ mod_export int -deletebuiltins(char const *nam, Builtin binl, int size) +addhookdefs(char const *nam, Hookdef h, int size) { - int hads = 0, hadf = 0, n; + int hads = 0, hadf = 0; - 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); + while (size--) { + if (addhookdef(h)) { + zwarnnam(nam, "name clash when adding hook `%s'", h->name); hadf = 1; } else hads = 2; - b->node.flags &= ~BINF_ADDED; + h++; } return hadf ? hads : 1; } -/* This removes the given wrapper definition from the list. Returned is * - * one in case of error and zero otherwise. */ +/* Delete hook definitions. */ /**/ -mod_export int -deletewrapper(Module m, FuncWrap w) +int +deletehookdef(Hookdef h) { - FuncWrap p, q; + Hookdef p, q; - if (m->flags & MOD_ALIAS) + for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next); + + if (!p) return 1; - if (w->flags & WRAPF_ADDED) { - for (p = wrappers, q = NULL; p && p != w; q = p, p = p->next); + if (q) + q->next = p->next; + else + hooktab = p->next; + freelinklist(p->funcs, NULL); + return 0; +} - if (p) { - if (q) - q->next = p->next; - else - wrappers = p->next; - p->flags &= ~WRAPF_ADDED; +/**/ +mod_export int +deletehookdefs(UNUSED(char const *nam), Hookdef h, int size) +{ + while (size--) { + deletehookdef(h); + h++; + } + return 1; +} +/* Add a function to a hook. */ + +/**/ +int +addhookdeffunc(Hookdef h, Hookfn f) +{ + zaddlinknode(h->funcs, (void *) f); + + return 0; +} + +/**/ +mod_export int +addhookfunc(char *n, Hookfn f) +{ + Hookdef h = gethookdef(n); + + if (h) + return addhookdeffunc(h, f); + return 1; +} + +/* Delete a function from a hook. */ + +/**/ +int +deletehookdeffunc(Hookdef h, Hookfn f) +{ + LinkNode p; + + for (p = firstnode(h->funcs); p; incnode(p)) + if (f == (Hookfn) getdata(p)) { + remnode(h->funcs, p); return 0; } - } return 1; } /**/ +mod_export int +deletehookfunc(char *n, Hookfn f) +{ + Hookdef h = gethookdef(n); + + if (h) + return deletehookdeffunc(h, f); + return 1; +} + +/* Run the function(s) for a hook. */ + +/**/ +mod_export int +runhookdef(Hookdef h, void *d) +{ + if (empty(h->funcs)) { + if (h->def) + return h->def(h, d); + return 0; + } else if (h->flags & HOOKF_ALL) { + LinkNode p; + int r; + + for (p = firstnode(h->funcs); p; incnode(p)) + if ((r = ((Hookfn) getdata(p))(h, d))) + return r; + if (h->def) + return h->def(h, d); + return 0; + } else + return ((Hookfn) getdata(lastnode(h->funcs)))(h, d); +} + +/**/ +int +runhook(char *n, void *d) +{ + Hookdef h = gethookdef(n); + + if (h) + return runhookdef(h, d); + return 0; +} + + +/************************************************************************ + * Shell parameters. + ************************************************************************/ + +static int +checkaddparam(char *nam) +{ + Param pm; + + 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; + } + + unsetparam_pm(pm, 0, 1); + return 0; +} + +/* This adds the given parameter definition. The return value is zero on * + * success and 1 on failure. */ + +/**/ +int +addparamdef(Paramdef d) +{ + Param pm; + + if (checkaddparam(d->name)) + 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. */ + +/**/ +int +deleteparamdef(Paramdef d) +{ + Param pm = (Param) paramtab->getnode(paramtab, d->name); + + if (!pm) + return 1; + if (pm != d->pm) { + /* + * See if the parameter has been hidden. If so, + * bring it to the front to unset it. + */ + Param prevpm, searchpm; + for (prevpm = pm, searchpm = pm->old; + searchpm; + prevpm = searchpm, searchpm = searchpm->old) + if (searchpm == d->pm) + break; + + if (!searchpm) + return 1; + + paramtab->removenode(paramtab, pm->node.nam); + prevpm->old = searchpm->old; + searchpm->old = pm; + paramtab->addnode(paramtab, searchpm->node.nam, searchpm); + + pm = searchpm; + } + pm->node.flags = (pm->node.flags & ~PM_READONLY) | PM_REMOVABLE; + unsetparam_pm(pm, 0, 1); + d->pm = NULL; + return 0; +} + +/**/ +mod_export int +setparamdefs(char const *nam, Paramdef d, int size, int *e) +{ + 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; +} + +/* This adds a definition for autoloading a module for a parameter. */ + +/**/ +void +add_autoparam(char *nam, char *module) +{ + 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. */ + +/**/ +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) +{ + 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); + + (void)load_module_silence(n, NULL, 0); + + return getmathfunc(name, 0); + } + return p; + } + + return NULL; +} + +/**/ +static int +addmathfunc(MathFunc f) +{ + MathFunc p, q = NULL; + + if (f->flags & MFF_ADDED) + return 1; + + for (p = mathfuncs; p; q = p, p = p->next) + if (!strcmp(f->name, p->name)) { + if (p->module && !(p->flags & MFF_USERFUNC)) { + /* + * Autoloadable, replace. + */ + removemathfunc(q, p); + break; + } + return 1; + } + + f->next = mathfuncs; + mathfuncs = f; + + return 0; +} + +/**/ +mod_export int +deletemathfunc(MathFunc f) +{ + MathFunc p, q; + + for (p = mathfuncs, q = NULL; p && p != f; q = p, p = p->next); + + if (p) { + if (q) + q->next = f->next; + else + mathfuncs = f->next; + + /* the following applies to both unloaded and user-defined functions */ + if (f->module) { + zsfree(f->name); + zsfree(f->module); + zfree(f, sizeof(*f)); + } else + f->flags &= ~MFF_ADDED; + + return 0; + } + return -1; +} + +/**/ +mod_export int +setmathfuncs(char const *nam, MathFunc f, int size, int *e) +{ + int hads = 0, hadf = 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; + } + } + f++; + } + return hadf ? hads : 1; +} + +/**/ +int +add_automathfunc(char *nam, char *module) +{ + MathFunc f = (MathFunc) zalloc(sizeof(*f)); + + f->name = ztrdup(nam); + f->module = ztrdup(module); + f->flags = 0; + + if (addmathfunc(f)) { + zsfree(f->name); + zsfree(f->module); + zfree(f, sizeof(*f)); + + return 1; + } + + return 0; +} + + +/************************************************************************ + * Now support for dynamical loading and the fallback functions + * we use for loading if dynamical loading is not available. + ************************************************************************/ + +/**/ #ifdef DYNAMIC /**/ @@ -392,11 +1123,15 @@ hpux_dlsym(void *handle, char *name) #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_" @@ -542,38 +1277,52 @@ module_loaded(const char *name) static int dyn_setup_module(Module m) { - return ((int (*)_((int,Module))) m->u.handle)(0, 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 +dyn_enables_module(Module m, int **enables) +{ + return ((int (*)_((int,Module))) m->u.handle)(5, m, enables); } /**/ static int dyn_boot_module(Module m) { - return ((int (*)_((int,Module))) m->u.handle)(1, m); + return ((int (*)_((int,Module))) m->u.handle)(1, m, NULL); } /**/ static int dyn_cleanup_module(Module m) { - return ((int (*)_((int,Module))) m->u.handle)(2, m); + return ((int (*)_((int,Module))) m->u.handle)(2, m, NULL); } /**/ static int dyn_finish_module(Module m) { - return ((int (*)_((int,Module))) m->u.handle)(3, m); + return ((int (*)_((int,Module))) m->u.handle)(3, m, NULL); } /**/ #else -static Module_func +static Module_generic_func module_func(Module m, char *name) { #ifdef DYNAMIC_NAME_CLASH_OK - return (Module_func) dlsym(m->u.handle, name); + 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; @@ -594,7 +1343,7 @@ module_func(Module m, char *name) *q++ = *p; } *q = 0; - return (Module_func) dlsym(m->u.handle, buf); + return (Module_generic_func) dlsym(m->u.handle, buf); #endif /* !DYNAMIC_NAME_CLASH_OK */ } @@ -602,7 +1351,7 @@ module_func(Module m, char *name) static int dyn_setup_module(Module m) { - Module_func fn = module_func(m, STR_SETUP); + Module_void_func fn = (Module_void_func)module_func(m, STR_SETUP); if (fn) return fn(m); @@ -612,9 +1361,34 @@ dyn_setup_module(Module m) /**/ static int +dyn_features_module(Module m, char ***features) +{ + Module_features_func fn = + (Module_features_func)module_func(m, STR_FEATURES); + + if (fn) + return fn(m, features); + /* not a user-visible error if no features function */ + return 1; +} + +/**/ +static int +dyn_enables_module(Module m, int **enables) +{ + Module_enables_func fn = (Module_enables_func)module_func(m, STR_ENABLES); + + if (fn) + return fn(m, enables); + /* not a user-visible error if no enables function */ + return 1; +} + +/**/ +static int dyn_boot_module(Module m) { - Module_func fn = module_func(m, STR_BOOT); + Module_void_func fn = (Module_void_func)module_func(m, STR_BOOT); if(fn) return fn(m); @@ -626,7 +1400,7 @@ dyn_boot_module(Module m) static int dyn_cleanup_module(Module m) { - Module_func fn = module_func(m, STR_CLEANUP); + Module_void_func fn = (Module_void_func)module_func(m, STR_CLEANUP); if(fn) return fn(m); @@ -641,7 +1415,7 @@ dyn_cleanup_module(Module m) static int dyn_finish_module(Module m) { - Module_func fn = module_func(m, STR_FINISH); + Module_void_func fn = (Module_void_func)module_func(m, STR_FINISH); int r; if (fn) @@ -667,6 +1441,24 @@ setup_module(Module m) /**/ static int +features_module(Module m, char ***features) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->features)(m, features) : + dyn_features_module(m, features)); +} + +/**/ +static int +enables_module(Module m, int **enables) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->enables)(m, enables) : + dyn_enables_module(m, enables)); +} + +/**/ +static int boot_module(Module m) { return ((m->flags & MOD_LINKED) ? @@ -701,6 +1493,22 @@ setup_module(Module m) /**/ static int +features_module(Module m, char ***features) +{ + return ((m->flags & MOD_LINKED) ? (m->u.linked->features)(m, features) + : 1); +} + +/**/ +static int +enables_module(Module m, int **enables) +{ + return ((m->flags & MOD_LINKED) ? (m->u.linked->enables)(m, enables) + : 1); +} + +/**/ +static int boot_module(Module m) { return ((m->flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1); @@ -723,6 +1531,125 @@ finish_module(Module m) /**/ #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 +do_module_features(Module m, char **enablesstr, int silent) +{ + 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; + } + + 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; + } + + 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; +} + +/* + * Boot the module, including setting up features. + * As we've only just loaded the module, we don't yet + * know what features it supports, so we get them passed + * as a string. + * + * Returns 0 if OK, 1 if completely failed, 2 if some features + * couldn't be set up. + */ + +/**/ +static int +do_boot_module(Module m, char **enablesstr, int silent) +{ + int ret = do_module_features(m, enablesstr, silent); + + if (ret == 1) + return 1; + + if (boot_module(m)) + return 1; + return ret; +} + +/* + * Cleanup the module. + */ + +/**/ +static int +do_cleanup_module(Module m) +{ + return (m->flags & MOD_LINKED) ? + (m->u.linked && m->u.linked->cleanup(m)) : + (m->u.handle && cleanup_module(m)); +} + /**/ static int modname_ok(char const *p) @@ -735,27 +1662,37 @@ modname_ok(char const *p) return 0; } +/* + * Now returns 0 for success (changed post-4.3.4), + * 1 for complete failure, 2 if some features couldn't be set. + */ + /**/ mod_export int -load_module(char const *name) +load_module(char const *name, char **enablesstr) { - return load_module_silence(name, 0); + return load_module_silence(name, enablesstr, 0); } +/* + * Returns 0 for success (changed post-4.3.4), 1 for complete + * failure, 2 if some features couldn't be set. + */ + /**/ mod_export int -load_module_silence(char const *name, int silent) +load_module_silence(char const *name, char **enablesstr, int silent) { Module m; void *handle = NULL; Linkedmod linked; LinkNode node, n; - int set; + int set, bootret; if (!modname_ok(name)) { if (!silent) zerr("invalid module name `%s'", name); - return 0; + return 1; } /* * The following function call may alter name to the final name in a @@ -767,7 +1704,7 @@ load_module_silence(char const *name, int silent) if (!(linked = module_linked(name)) && !(handle = do_load_module(name, silent))) { unqueue_signals(); - return 0; + return 1; } m = zshcalloc(sizeof(*m)); m->nam = ztrdup(name); @@ -780,40 +1717,47 @@ load_module_silence(char const *name, int silent) } node = zaddlinknode(modules, m); - if ((set = setup_module(m)) || boot_module(m)) { - if (!set) + 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 0; + return 1; } m->flags |= MOD_INIT_S | MOD_INIT_B; m->flags &= ~MOD_SETUP; unqueue_signals(); - return 1; - } + return bootret; + } m = (Module) getdata(node); if (m->flags & MOD_SETUP) { unqueue_signals(); - return 1; + 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 1; + return 0; } if (m->flags & MOD_BUSY) { zerr("circular dependencies for module %s", name); - return 0; + 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), silent)) { + if (load_module_silence((char *) getdata(n), NULL, silent) == 1) { m->flags &= ~MOD_BUSY; unqueue_signals(); - return 0; + return 1; } m->flags &= ~MOD_BUSY; if (!m->u.handle) { @@ -821,7 +1765,7 @@ load_module_silence(char const *name, int silent) if (!(linked = module_linked(name)) && !(handle = do_load_module(name, silent))) { unqueue_signals(); - return 0; + return 1; } if (handle) { m->u.handle = handle; @@ -837,12 +1781,13 @@ load_module_silence(char const *name, int silent) m->u.linked = NULL; m->flags &= ~MOD_SETUP; unqueue_signals(); - return 0; + return 1; } m->flags |= MOD_INIT_S; } m->flags |= MOD_SETUP; - if (boot_module(m)) { + 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; @@ -850,42 +1795,44 @@ load_module_silence(char const *name, int silent) m->u.handle = NULL; m->flags &= ~MOD_SETUP; unqueue_signals(); - return 0; + return 1; } m->flags |= MOD_INIT_B; m->flags &= ~MOD_SETUP; unqueue_signals(); - return 1; + return bootret; } /* 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. */ + * 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. + */ /**/ mod_export int -require_module(char *nam, const char *module, UNUSED(int res), int test) +require_module(char *nam, const char *module, char **features) { Module m = NULL; LinkNode node; - int ret = 1; + 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)) { - if (test) { - unqueue_signals(); - zwarnnam(nam, "module %s already loaded.", module); - return 0; - } - } else - ret = load_module_silence(module, 0); + 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(); return ret; @@ -953,6 +1900,11 @@ autoloadscan(HashNode hn, int printflags) putchar('\n'); } + +/************************************************************************ + * Handling for the zmodload builtin and its various options. + ************************************************************************/ + /**/ int bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) @@ -961,10 +1913,18 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) 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 (ops_bcpf && !ops_au) { - zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u"); - return 1; + 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') || @@ -987,10 +1947,19 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) 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,'e')) + 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); @@ -1471,9 +2440,7 @@ unload_module(Module m, LinkNode 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)))) + do_cleanup_module(m)) return 1; else { int del = (m->flags & MOD_UNLOAD); @@ -1622,608 +2589,274 @@ bin_zmodload_load(char *nam, char **args, Options ops) return 0; } else { /* load modules */ - for (; *args; args++) - if (!require_module(nam, *args, 1, (!OPT_ISSET(ops,'i')))) - ret = 1; - - return ret; - } -} - -/* The list of module-defined conditions. */ - -/**/ -mod_export Conddef condtab; - -/* This gets a condition definition with the given name. The first * - * argument says if we have to look for an infix condition. The last * - * argument is non-zero if we should autoload modules if needed. */ - -/**/ -Conddef -getconddef(int inf, char *name, int autol) -{ - Conddef p; - int f = 1; - - do { - for (p = condtab; p; p = p->next) { - if ((!!inf == !!(p->flags & CONDF_INFIX)) && - !strcmp(name, p->name)) - break; - } - if (autol && p && p->module) { - /* This is a definition for an autoloaded condition, load the * - * module if we haven't tried that already. */ - if (f) { - load_module_silence(p->module, 0); - f = 0; - p = NULL; - } else { - deleteconddef(p); - return NULL; - } - } else - break; - } while (!p); - return p; -} - -/* This adds the given condition definition. The return value is zero on * - * success and 1 on failure. If there is a matching definition for an * - * autoloaded condition, it is removed. */ - -/**/ -int -addconddef(Conddef c) -{ - Conddef p = getconddef((c->flags & CONDF_INFIX), c->name, 0); - - if (p) { - if (!p->module || (p->flags & CONDF_ADDED)) - return 1; - /* There is an autoload definition. */ - - deleteconddef(p); - } - c->next = condtab; - condtab = c; - return 0; -} - -/* This adds multiple condition definitions. This is like addbuiltins(). */ - -/**/ -mod_export int -addconddefs(char const *nam, Conddef c, int size) -{ - int hads = 0, hadf = 0; - - while (size--) { - if (c->flags & CONDF_ADDED) { - c++; - continue; - } - if (addconddef(c)) { - zwarnnam(nam, "name clash when adding condition `%s'", c->name); - hadf = 1; - } else { - c->flags |= CONDF_ADDED; - hads = 2; + for (; *args; args++) { + int tmpret = require_module(nam, *args, NULL); + if (tmpret && ret != 1) + ret = tmpret; } - c++; - } - return hadf ? hads : 1; -} - -/* This list of hook functions defined. */ -/**/ -Hookdef hooktab; - -/* Find a hook definition given the name. */ - -/**/ -Hookdef -gethookdef(char *n) -{ - Hookdef p; - - for (p = hooktab; p; p = p->next) - if (!strcmp(n, p->name)) - return p; - return NULL; -} - -/* This adds the given hook definition. The return value is zero on * - * success and 1 on failure. */ - -/**/ -int -addhookdef(Hookdef h) -{ - if (gethookdef(h->name)) - return 1; - - h->next = hooktab; - hooktab = h; - h->funcs = znewlinklist(); - - return 0; -} - -/* This adds multiple hook definitions. This is like addbuiltins(). */ - -/**/ -mod_export int -addhookdefs(char const *nam, Hookdef h, int size) -{ - int hads = 0, hadf = 0; - - while (size--) { - if (addhookdef(h)) { - zwarnnam(nam, "name clash when adding hook `%s'", h->name); - hadf = 1; - } else - hads = 2; - h++; + return ret; } - return hadf ? hads : 1; } -/* Delete hook definitions. */ - /**/ -int -deletehookdef(Hookdef h) +static int +bin_zmodload_features(char *nam, char **args, Options ops) { - Hookdef p, q; + char *modname = *args; - for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next); - - if (!p) + if (!modname) { + zwarnnam(nam, "-F requires a module name"); return 1; - - if (q) - q->next = p->next; - else - hooktab = p->next; - freelinklist(p->funcs, NULL); - return 0; -} - -/**/ -mod_export int -deletehookdefs(UNUSED(char const *nam), Hookdef h, int size) -{ - while (size--) { - deletehookdef(h); - h++; } - return 1; -} - -/* Add a function to a hook. */ - -/**/ -int -addhookdeffunc(Hookdef h, Hookfn f) -{ - zaddlinknode(h->funcs, (void *) f); - - return 0; -} - -/**/ -mod_export int -addhookfunc(char *n, Hookfn f) -{ - Hookdef h = gethookdef(n); - - if (h) - return addhookdeffunc(h, f); - return 1; -} - -/* Delete a function from a hook. */ - -/**/ -int -deletehookdeffunc(Hookdef h, Hookfn f) -{ - LinkNode p; - - for (p = firstnode(h->funcs); p; incnode(p)) - if (f == (Hookfn) getdata(p)) { - remnode(h->funcs, p); - return 0; - } - return 1; -} - -/**/ -mod_export int -deletehookfunc(char *n, Hookfn f) -{ - Hookdef h = gethookdef(n); - - if (h) - return deletehookdeffunc(h, f); - return 1; -} - -/* Run the function(s) for a hook. */ - -/**/ -mod_export int -runhookdef(Hookdef h, void *d) -{ - if (empty(h->funcs)) { - if (h->def) - return h->def(h, d); - return 0; - } else if (h->flags & HOOKF_ALL) { - LinkNode p; - int r; - - for (p = firstnode(h->funcs); p; incnode(p)) - if ((r = ((Hookfn) getdata(p))(h, d))) - return r; - if (h->def) - return h->def(h, d); - return 0; - } else - return ((Hookfn) getdata(lastnode(h->funcs)))(h, d); -} - -/**/ -int -runhook(char *n, void *d) -{ - Hookdef h = gethookdef(n); - - if (h) - return runhookdef(h, d); - return 0; -} - -/* This adds the given parameter definition. The return value is zero on * - * success and 1 on failure. */ - -/**/ -int -addparamdef(Paramdef d) -{ - Param pm; - - if ((pm = (Param) gethashnode2(paramtab, d->name))) - unsetparam_pm(pm, 0, 1); - - if (!(pm = createparam(d->name, d->flags)) && - !(pm = (Param) paramtab->getnode(paramtab, d->name))) - return 1; + args++; - pm->level = 0; - pm->u.data = d->var; - if (d->gsu) - pm->gsu.i = (GsuInteger) d->gsu; - else { + if (OPT_ISSET(ops,'l') || OPT_ISSET(ops,'L') || OPT_ISSET(ops,'e')) { /* - * If no get/set/unset class, use the appropriate - * variable type. + * 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. */ - switch (PM_TYPE(pm->node.flags)) { - case PM_SCALAR: - pm->gsu.s = &varscalar_gsu; - break; - - case PM_INTEGER: - pm->gsu.i = &varinteger_gsu; - break; - - case PM_ARRAY: - pm->gsu.a = &vararray_gsu; - break; - - default: - unsetparam_pm(pm, 0, 1); + 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; } - } - - return 0; -} - -/* This adds multiple parameter definitions. This is like addbuiltins(). */ - -/**/ -mod_export int -addparamdefs(char const *nam, Paramdef d, int size) -{ - int hads = 0, hadf = 0; - - while (size--) { - if (addparamdef(d)) { - zwarnnam(nam, "error when adding parameter `%s'", d->name); - hadf = 1; - } else - hads = 2; - d++; - } - return hadf ? hads : 1; -} - -/* Delete parameters defined. No error checking yet. */ - -/**/ -int -deleteparamdef(Paramdef d) -{ - unsetparam(d->name); - return 0; -} - -/**/ -mod_export int -deleteparamdefs(UNUSED(char const *nam), Paramdef d, int size) -{ - while (size--) { - deleteparamdef(d); - d++; - } - return 1; -} - -/* This adds a definition for autoloading a module for a condition. */ - -/**/ -int -add_autocond(char *nam, int inf, char *module) -{ - Conddef c = (Conddef) zalloc(sizeof(*c)); - - c->name = ztrdup(nam); - c->flags = (inf ? CONDF_INFIX : 0); - c->module = ztrdup(module); - - if (addconddef(c)) { - zsfree(c->name); - zsfree(c->module); - zfree(c, sizeof(*c)); - - return 1; - } - return 0; -} - -/* This removes the given condition definition from the list(s). If this * - * is a definition for a autoloaded condition, the memory is freed. */ - -/**/ -int -deleteconddef(Conddef c) -{ - Conddef p, q; - - for (p = condtab, q = NULL; p && p != c; q = p, p = p->next); - - if (p) { - if (q) - q->next = p->next; - else - condtab = p->next; - - if (p->module) { - /* autoloaded, free it */ - zsfree(p->name); - zsfree(p->module); - zfree(p, sizeof(*p)); + if (features_module(m, &features)) { + if (!OPT_ISSET(ops,'e')) + zwarnnam(nam, "module `%s' does not support features", m->nam); + return 1; } - return 0; - } - return -1; -} - -/* This removes multiple condition definitions (like deletebuiltins()). */ - -/**/ -mod_export int -deleteconddefs(char const *nam, Conddef c, int size) -{ - int hads = 0, hadf = 0; - - while (size--) { - if (!(c->flags & CONDF_ADDED)) { - c++; - continue; + if (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; } - if (deleteconddef(c)) { - zwarnnam(nam, "condition `%s' already deleted", c->name); - hadf = 1; - } else - hads = 2; - c->flags &= ~CONDF_ADDED; - c++; + 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; } - return hadf ? hads : 1; -} - -/* This adds a definition for autoloading a module for a parameter. */ -/**/ -void -add_autoparam(char *nam, char *module) -{ - Param pm; - - queue_signals(); - if ((pm = (Param) gethashnode2(paramtab, nam))) - unsetparam_pm(pm, 0, 1); - - pm = setsparam(nam, ztrdup(module)); - - pm->node.flags |= PM_AUTOLOAD; - unqueue_signals(); + return require_module(nam, modname, args); } -/* List of math functions. */ -/**/ -MathFunc mathfuncs; +/************************************************************************ + * Generic feature support. + * These functions are designed to be called by modules. + ************************************************************************/ -/**/ -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)); -} +/* + * 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. + */ /**/ -MathFunc -getmathfunc(char *name, int autol) +mod_export char ** +featuresarray(char const *nam, Features f) { - 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); + 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; - return getmathfunc(name, 0); - } - return p; - } + 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); - return NULL; + features[features_size] = NULL; + return features; } +/* + * Return the current set of enables for the features in a + * module using heap memory. Leave space for abstract + * features. The array is not zero terminated. + */ /**/ -mod_export int -addmathfunc(MathFunc f) +mod_export int * +getfeatureenables(char const *nam, Features f) { - MathFunc p, q = NULL; - - if (f->flags & MFF_ADDED) - return 1; - - for (p = mathfuncs; p; q = p, p = p->next) - if (!strcmp(f->name, p->name)) { - if (p->module && !(p->flags & MFF_USERFUNC)) { - /* - * Autoloadable, replace. - */ - removemathfunc(q, p); - break; - } - return 1; - } + 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; - f->flags |= MFF_ADDED; - f->next = mathfuncs; - mathfuncs = f; + 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; } -/**/ -mod_export int -addmathfuncs(char const *nam, MathFunc f, int size) -{ - int hads = 0, hadf = 0; - - while (size--) { - if (f->flags & MFF_ADDED) { - f++; - continue; - } - if (addmathfunc(f)) { - zwarnnam(nam, "name clash when adding math function `%s'", - f->name); - hadf = 1; - } else - hads = 2; - f++; - } - return hadf ? hads : 1; -} - -/**/ -int -add_automathfunc(char *nam, char *module) -{ - MathFunc f = (MathFunc) zalloc(sizeof(*f)); - - f->name = ztrdup(nam); - f->module = ztrdup(module); - f->flags = 0; - - if (addmathfunc(f)) { - zsfree(f->name); - zsfree(f->module); - zfree(f, sizeof(*f)); - - return 1; - } - f->flags &= ~MFF_ADDED; /* still to autoload, not added yet */ - - return 0; -} +/* + * 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; } |