diff options
author | Peter Stephenson <pws@users.sourceforge.net> | 2007-07-06 21:52:38 +0000 |
---|---|---|
committer | Peter Stephenson <pws@users.sourceforge.net> | 2007-07-06 21:52:38 +0000 |
commit | 1b52f47cf285d5f3835bce7ad73f360bd327d4e8 (patch) | |
tree | af5f6637517084bc7914dacfc7fda0a5799f3220 /Src/module.c | |
parent | 018c9a2708808b83d5962786f759a931ab27511d (diff) | |
download | zsh-1b52f47cf285d5f3835bce7ad73f360bd327d4e8.tar.gz zsh-1b52f47cf285d5f3835bce7ad73f360bd327d4e8.tar.xz zsh-1b52f47cf285d5f3835bce7ad73f360bd327d4e8.zip |
23665: autoloading of module features and related tweaks
Diffstat (limited to 'Src/module.c')
-rw-r--r-- | Src/module.c | 1239 |
1 files changed, 820 insertions, 419 deletions
diff --git a/Src/module.c b/Src/module.c index c82d77373..765f7c941 100644 --- a/Src/module.c +++ b/Src/module.c @@ -24,13 +24,16 @@ * provided hereunder is on an "as is" basis, and Zoltán Hidvégi and the * Zsh Development Group have no obligation to provide maintenance, * support, updates, enhancements, or modifications. - * */ #include "zsh.mdh" #include "module.pro" -/* List of linked-in modules. */ +/* + * List of linked-in modules. + * This is set up at boot and remains for the life of the shell; + * entries do not appear in "zmodload" listings. + */ /**/ LinkList linkedmodules; @@ -40,19 +43,42 @@ LinkList linkedmodules; /**/ char **module_path; -/* List of modules */ +/* Hash of modules */ /**/ -mod_export LinkList modules; +mod_export HashTable modulestab; /* * Bit flags passed as the "flags" argument of a autofeaturefn_t. + * Used in other places, such as the final argument to + * do_module_features(). */ enum { - /* `-i' option: ignore errors pertaining to redefinitions */ - AUTOFEAT_IGNORE = 0x0001, + /* + * `-i' option: ignore errors pertaining to redefinitions, + * or indicate to do_module_features() that it should be + * silent. + */ + FEAT_IGNORE = 0x0001, /* If a condition, condition is infix rather than prefix */ - AUTOFEAT_INFIX = 0x0002 + FEAT_INFIX = 0x0002, + /* + * Enable all features in the module when autoloading. + * This is the traditional zmodload -a behaviour; + * zmodload -Fa only enables features explicitly marked for + * autoloading. + */ + FEAT_AUTOALL = 0x0004, + /* + * Remove feature: alternative to "-X:NAME" used if + * X is passed separately from NAME. + */ + FEAT_REMOVE = 0x0008, + /* + * For do_module_features(). Check that any autoloads + * for the module are actually provided. + */ + FEAT_CHECKAUTO = 0x0010 }; /* @@ -65,7 +91,7 @@ enum { * * "flags" is a set of the bits above. * - * The return value is 0 for success, a negative value for failure with no + * The return value is 0 for success, -1 for failure with no * message needed, and one of the following to indicate the calling * function should print a message: * @@ -73,7 +99,199 @@ enum { * 2: [feature]: no such [type] * 3: [feature]: [type] is already defined */ -typedef int (*autofeaturefn_t)(char *module, char *feature, int flags); +typedef int (*autofeaturefn_t)(const char *module, const char *feature, + int flags); + +/* Bits in the second argument to find_module. */ +enum { + /* + * Resolve any aliases to the underlying module. + */ + FINDMOD_ALIASP = 0x0001, + /* + * Create an element for the module in the list if + * it is not found. + */ + FINDMOD_CREATE = 0x0002, +}; + +static void +freemodulenode(HashNode hn) +{ + Module m = (Module) hn; + + if (m->node.flags & MOD_ALIAS) + zsfree(m->u.alias); + zsfree(m->node.nam); + if (m->autoloads) + freelinklist(m->autoloads, freestr); + if (m->deps) + freelinklist(m->deps, freestr); + zfree(m, sizeof(*m)); +} + +/* flags argument to printmodulenode */ +enum { + /* -L flag, output zmodload commands */ + PRINTMOD_LIST = 0x0001, + /* -e flag */ + PRINTMOD_EXIST = 0x0002, + /* -A flag */ + PRINTMOD_ALIAS = 0x0004, + /* -d flag */ + PRINTMOD_DEPS = 0x0008, + /* -F flag */ + PRINTMOD_FEATURES = 0x0010, + /* -l flag in combination with -L flag */ + PRINTMOD_LISTALL = 0x0020, + /* -a flag */ + PRINTMOD_AUTO = 0x0040 +}; + +/* Scan function for printing module details */ + +static void +printmodulenode(HashNode hn, int flags) +{ + Module m = (Module)hn; + /* + * If we check for a module loaded under an alias, we + * need the name of the alias. We can use it in other + * cases, too. + */ + const char *modname = m->node.nam; + + if (flags & PRINTMOD_DEPS) { + /* + * Print the module's dependencies. + */ + LinkNode n; + + if (!m->deps) + return; + + if (flags & PRINTMOD_LIST) { + printf("zmodload -d "); + if (modname[0] == '-') + fputs("-- ", stdout); + quotedzputs(modname, stdout); + } else { + nicezputs(modname, stdout); + putchar(':'); + } + for (n = firstnode(m->deps); n; incnode(n)) { + putchar(' '); + if (flags & PRINTMOD_LIST) + quotedzputs((char *) getdata(n), stdout); + else + nicezputs((char *) getdata(n), stdout); + } + } else if (flags & PRINTMOD_EXIST) { + /* + * Just print the module name, provided the module is + * present under an alias or otherwise. + */ + if (m->node.flags & MOD_ALIAS) { + if (!(flags & PRINTMOD_ALIAS) || + !(m = find_module(m->u.alias, FINDMOD_ALIASP, NULL))) + return; + } + if (!m->u.handle || (m->node.flags & MOD_UNLOAD)) + return; + nicezputs(modname, stdout); + } else if (m->node.flags & MOD_ALIAS) { + /* + * Normal listing, but for aliases. + */ + if (flags & PRINTMOD_LIST) { + printf("zmodload -A "); + if (modname[0] == '-') + fputs("-- ", stdout); + quotedzputs(modname, stdout); + putchar('='); + quotedzputs(m->u.alias, stdout); + } else { + nicezputs(modname, stdout); + fputs(" -> ", stdout); + nicezputs(m->u.alias, stdout); + } + } else if (m->u.handle) { + /* + * Loaded module. + */ + if (flags & PRINTMOD_LIST) { + /* + * List with -L format. Possibly we are printing + * features, either enables or autoloads. + */ + char **features = NULL; + int *enables = NULL; + if (flags & PRINTMOD_AUTO) { + if (!m->autoloads || !firstnode(m->autoloads)) + return; + } else if (flags & PRINTMOD_FEATURES) { + if (features_module(m, &features) || + enables_module(m, &enables) || + !*features) + return; + } + printf("zmodload "); + if (flags & PRINTMOD_AUTO) { + fputs("-Fa ", stdout); + } else if (features) + fputs("-F ", stdout); + if(modname[0] == '-') + fputs("-- ", stdout); + quotedzputs(modname, stdout); + if (flags & PRINTMOD_AUTO) { + LinkNode an; + for (an = firstnode(m->autoloads); an; incnode(an)) { + putchar(' '); + quotedzputs((char *)getdata(an), stdout); + } + } else if (features) { + const char *f; + while ((f = *features++)) { + int on = *enables++; + if (flags & PRINTMOD_LISTALL) + printf(" %s", on ? "+" : "-"); + else if (!on) + continue; + else + putchar(' '); + quotedzputs(f, stdout); + } + } + } else /* -l */ + nicezputs(modname, stdout); + } else + return; + putchar('\n'); +} + +/**/ +HashTable +newmoduletable(int size, char const *name) +{ + HashTable ht; + ht = newhashtable(size, name, NULL); + + ht->hash = hasher; + ht->emptytable = emptyhashtable; + ht->filltable = NULL; + ht->cmpnodes = strcmp; + ht->addnode = addhashnode; + /* DISABLED is not supported */ + ht->getnode = gethashnode2; + ht->getnode2 = gethashnode2; + ht->removenode = removehashnode; + ht->disablenode = NULL; + ht->enablenode = NULL; + ht->freenode = freemodulenode; + ht->printnode = printmodulenode; + + return ht; +} /************************************************************************ * zsh/main standard module functions @@ -160,27 +378,6 @@ register_module(char *n, Module_void_func setup, zaddlinknode(linkedmodules, m); } -/* Print an alias. */ - -/**/ -static void -printmodalias(Module m, Options ops) -{ - if (OPT_ISSET(ops,'L')) { - printf("zmodload -A "); - if (m->nam[0] == '-') - fputs("-- ", stdout); - quotedzputs(m->nam, stdout); - putchar('='); - quotedzputs(m->u.alias, stdout); - } else { - nicezputs(m->nam, stdout); - fputs(" -> ", stdout); - nicezputs(m->u.alias, stdout); - } - putchar('\n'); -} - /* Check if a module is linked in. */ /**/ @@ -208,7 +405,7 @@ module_linked(char const *name) * builtin can be replaced using this function. */ /**/ -int +static int addbuiltin(Builtin b) { Builtin bn = (Builtin) builtintab->getnode2(builtintab, b->node.nam); @@ -226,16 +423,19 @@ addbuiltin(Builtin b) /**/ static int -add_autobin(char *module, char *bnam, int flags) +add_autobin(const char *module, const char *bnam, int flags) { Builtin bn; + int ret; bn = zshcalloc(sizeof(*bn)); bn->node.nam = ztrdup(bnam); bn->optstr = ztrdup(module); - if (addbuiltin(bn)) { + if (flags & FEAT_AUTOALL) + bn->node.flags |= BINF_AUTOALL; + if ((ret = addbuiltin(bn))) { builtintab->freenode(&bn->node); - if (!(flags & AUTOFEAT_IGNORE)) + if (!(flags & FEAT_IGNORE)) return 1; } return 0; @@ -246,7 +446,7 @@ add_autobin(char *module, char *bnam, int flags) /**/ int -deletebuiltin(char *nam) +deletebuiltin(const char *nam) { Builtin bn; @@ -261,14 +461,15 @@ deletebuiltin(char *nam) /**/ static int -del_autobin(UNUSED(char *module), char *bnam, int flags) +del_autobin(UNUSED(const char *module), const char *bnam, int flags) { Builtin bn = (Builtin) builtintab->getnode2(builtintab, bnam); if (!bn) { - if(!(flags & AUTOFEAT_IGNORE)) + if(!(flags & FEAT_IGNORE)) return 2; } else if (bn->node.flags & BINF_ADDED) { - return 3; + if (!(flags & FEAT_IGNORE)) + return 3; } else deletebuiltin(bnam); @@ -383,7 +584,7 @@ addwrapper(Module m, FuncWrap w) * happen since we usually add wrappers when a real module is * loaded. */ - if (m->flags & MOD_ALIAS) + if (m->node.flags & MOD_ALIAS) return 1; if (w->flags & WRAPF_ADDED) @@ -409,7 +610,7 @@ deletewrapper(Module m, FuncWrap w) { FuncWrap p, q; - if (m->flags & MOD_ALIAS) + if (m->node.flags & MOD_ALIAS) return 1; if (w->flags & WRAPF_ADDED) { @@ -444,7 +645,7 @@ mod_export Conddef condtab; /**/ Conddef -getconddef(int inf, char *name, int autol) +getconddef(int inf, const char *name, int autol) { Conddef p; int f = 1; @@ -462,8 +663,8 @@ getconddef(int inf, char *name, int autol) */ if (f) { (void)ensurefeature(p->module, - (p->flags & CONDF_INFIX) ? - "C:" : "c:", name); + (p->flags & CONDF_INFIX) ? "C:" : "c:", + (p->flags & CONDF_AUTOALL) ? NULL : name); f = 0; p = NULL; } else { @@ -577,14 +778,16 @@ setconddefs(char const *nam, Conddef c, int size, int *e) /**/ static int -add_autocond(char *module, char *cnam, int flags) +add_autocond(const char *module, const char *cnam, int flags) { Conddef c; c = (Conddef) zalloc(sizeof(*c)); c->name = ztrdup(cnam); - c->flags = ((flags & AUTOFEAT_INFIX) ? CONDF_INFIX : 0); + c->flags = ((flags & FEAT_INFIX) ? CONDF_INFIX : 0); + if (flags & FEAT_AUTOALL) + c->flags |= CONDF_AUTOALL; c->module = ztrdup(module); if (addconddef(c)) { @@ -592,7 +795,7 @@ add_autocond(char *module, char *cnam, int flags) zsfree(c->module); zfree(c, sizeof(*c)); - if (!(flags & AUTOFEAT_IGNORE)) + if (!(flags & FEAT_IGNORE)) return 1; } return 0; @@ -602,16 +805,17 @@ add_autocond(char *module, char *cnam, int flags) /**/ static int -del_autocond(UNUSED(char *modnam), char *cnam, int flags) +del_autocond(UNUSED(const char *modnam), const char *cnam, int flags) { - Conddef cd = getconddef((flags & AUTOFEAT_INFIX) ? 1 : 0, cnam, 0); + Conddef cd = getconddef((flags & FEAT_INFIX) ? 1 : 0, cnam, 0); if (!cd) { - if (!(flags & AUTOFEAT_IGNORE)) { + if (!(flags & FEAT_IGNORE)) { return 2; } } else if (cd->flags & CONDF_ADDED) { - return 3; + if (!(flags & FEAT_IGNORE)) + return 3; } else deleteconddef(cd); @@ -658,17 +862,21 @@ addhookdef(Hookdef h) return 0; } -/* This adds multiple hook definitions. This is like addbuiltins(). */ +/* + * This adds multiple hook definitions. This is like addbuiltins(). + * This allows a NULL module because we call it from init.c. + */ /**/ mod_export int -addhookdefs(char const *nam, Hookdef h, int size) +addhookdefs(Module m, Hookdef h, int size) { int ret = 0; while (size--) { if (addhookdef(h)) { - zwarnnam(nam, "name clash when adding hook `%s'", h->name); + zwarnnam(m ? m->node.nam : NULL, + "name clash when adding hook `%s'", h->name); ret = 1; } h++; @@ -701,7 +909,7 @@ deletehookdef(Hookdef h) /**/ mod_export int -deletehookdefs(UNUSED(char const *nam), Hookdef h, int size) +deletehookdefs(UNUSED(Module m), Hookdef h, int size) { int ret = 0; @@ -804,7 +1012,7 @@ runhookdef(Hookdef h, void *d) */ static int -checkaddparam(char *nam, int opt_i) +checkaddparam(const char *nam, int opt_i) { Param pm; @@ -970,13 +1178,13 @@ setparamdefs(char const *nam, Paramdef d, int size, int *e) /**/ static int -add_autoparam(char *module, char *pnam, int flags) +add_autoparam(const char *module, const char *pnam, int flags) { Param pm; int ret; queue_signals(); - if ((ret = checkaddparam(pnam, (flags & AUTOFEAT_IGNORE)))) { + if ((ret = checkaddparam(pnam, (flags & FEAT_IGNORE)))) { unqueue_signals(); /* * checkaddparam() has already printed a message if one was @@ -988,9 +1196,11 @@ add_autoparam(char *module, char *pnam, int flags) return ret == 2 ? 0 : -1; } - pm = setsparam(pnam, ztrdup(module)); + pm = setsparam(dupstring(pnam), ztrdup(module)); pm->node.flags |= PM_AUTOLOAD; + if (flags & FEAT_AUTOALL) + pm->node.flags |= PM_AUTOALL; unqueue_signals(); return 0; @@ -1000,15 +1210,16 @@ add_autoparam(char *module, char *pnam, int flags) /**/ static int -del_autoparam(UNUSED(char *modnam), char *pnam, int flags) +del_autoparam(UNUSED(const char *modnam), const char *pnam, int flags) { Param pm = (Param) gethashnode2(paramtab, pnam); if (!pm) { - if (!(flags & AUTOFEAT_IGNORE)) + if (!(flags & FEAT_IGNORE)) return 2; } else if (!(pm->node.flags & PM_AUTOLOAD)) { - return 3; + if (!(flags & FEAT_IGNORE)) + return 3; } else unsetparam_pm(pm, 0, 1); @@ -1047,7 +1258,7 @@ removemathfunc(MathFunc previous, MathFunc current) /**/ MathFunc -getmathfunc(char *name, int autol) +getmathfunc(const char *name, int autol) { MathFunc p, q = NULL; @@ -1058,7 +1269,8 @@ getmathfunc(char *name, int autol) removemathfunc(q, p); - (void)ensurefeature(n, "f:", name); + (void)ensurefeature(n, "f:", (p->flags & MFF_AUTOALL) ? NULL : + name); return getmathfunc(name, 0); } @@ -1171,7 +1383,7 @@ setmathfuncs(char const *nam, MathFunc f, int size, int *e) /**/ static int -add_automathfunc(char *module, char *fnam, int flags) +add_automathfunc(const char *module, const char *fnam, int flags) { MathFunc f; @@ -1186,7 +1398,7 @@ add_automathfunc(char *module, char *fnam, int flags) zsfree(f->module); zfree(f, sizeof(*f)); - if (!(flags & AUTOFEAT_IGNORE)) + if (!(flags & FEAT_IGNORE)) return 1; } @@ -1197,15 +1409,16 @@ add_automathfunc(char *module, char *fnam, int flags) /**/ static int -del_automathfunc(UNUSED(char *modnam), char *fnam, int flags) +del_automathfunc(UNUSED(const char *modnam), const char *fnam, int flags) { MathFunc f = getmathfunc(fnam, 0); if (!f) { - if (!(flags & AUTOFEAT_IGNORE)) + if (!(flags & FEAT_IGNORE)) return 2; } else if (f->flags & MFF_ADDED) { - return 3; + if (!(flags & FEAT_IGNORE)) + return 3; } else deletemathfunc(f); @@ -1233,13 +1446,15 @@ load_and_bind(const char *fn) void *ret = (void *) load((char *) fn, L_NOAUTODEFER, NULL); if (ret) { - LinkNode node; - int err = loadbind(0, (void *) addbuiltin, ret); - for (node = firstnode(modules); !err && node; incnode(node)) { - Module m = (Module) getdata(node); - if (!(m->flags & MOD_ALIAS) && - m->u.handle && !(m->flags & MOD_LINKED)) - err |= loadbind(0, m->u.handle, ret); + Module m; + int i, err = loadbind(0, (void *) addbuiltin, ret); + for (i = 0; i < modulestab->hsize && !err; i++) { + for (m = (Module)modulestab->nodes[i]; m && !err; + m = m->node.next) { + if (!(m->flags & MOD_ALIAS) && + m->u.handle && !(m->flags & MOD_LINKED)) + err |= loadbind(0, m->u.handle, ret); + } } if (err) { @@ -1256,6 +1471,9 @@ load_and_bind(const char *fn) #define dlopen(X,Y) load_and_bind(X) #define dlclose(X) unload(X) #define dlerror() (dlerrstr[0]) +#ifndef HAVE_DLERROR +# define HAVE_DLERROR 1 +#endif /**/ #else @@ -1294,7 +1512,9 @@ hpux_dlsym(void *handle, char *name) } # define dlsym(handle,name) hpux_dlsym(handle,name) -# define dlerror() 0 +# ifdef HAVE_DLERROR /* paranoia */ +# undef HAVE_DLERROR +# endif #else # ifndef HAVE_DLCLOSE # define dlclose(X) ((X), 0) @@ -1366,8 +1586,13 @@ do_load_module(char const *name, int silent) void *ret; ret = try_load_module(name); - if (!ret && !silent) + if (!ret && !silent) { +#ifdef HAVE_DLERROR + zwarn("failed to load module `%s': %s", name, dlerror()); +#else zwarn("failed to load module: %s", name); +#endif + } return ret; } @@ -1391,19 +1616,6 @@ do_load_module(char const *name, int silent) /**/ #endif /* !DYNAMIC */ -/* Bits in the second argument to find_module. */ -enum { - /* - * Resolve any aliases to the underlying module. - */ - FINDMOD_ALIASP = 0x0001, - /* - * Create an element for the module in the list if - * it is not found. - */ - FINDMOD_CREATE = 0x0002, -}; - /* * Find a module in the list. * flags is a set of bits defined in the enum above. @@ -1413,35 +1625,29 @@ enum { * 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 +static Module find_module(const char *name, int flags, const char **namep) { Module m; - LinkNode node; - for (node = firstnode(modules); node; incnode(node)) { - m = (Module) getdata(node); - if (!strcmp(m->nam, name)) { - if ((flags & FINDMOD_ALIASP) && (m->flags & MOD_ALIAS)) { - if (namep) - *namep = m->u.alias; - return find_module(m->u.alias, flags, namep); - } + m = (Module)modulestab->getnode2(modulestab, name); + if (m) { + if ((flags & FINDMOD_ALIASP) && (m->node.flags & MOD_ALIAS)) { if (namep) - *namep = m->nam; - return node; + *namep = m->u.alias; + return find_module(m->u.alias, flags, namep); } + if (namep) + *namep = m->node.nam; + return m; } if (!(flags & FINDMOD_CREATE)) return NULL; m = zshcalloc(sizeof(*m)); - m->nam = ztrdup(name); - return zaddlinknode(modules, m); + modulestab->addnode(modulestab, ztrdup(name), m); + return m; } /* @@ -1450,16 +1656,11 @@ find_module(const char *name, int flags, const char **namep) /**/ static void -delete_module(LinkNode node) +delete_module(Module m) { - Module m = (Module) remnode(modules, node); + modulestab->removenode(modulestab, m->node.nam); - if (m->flags & MOD_ALIAS) - zsfree(m->u.alias); - zsfree(m->nam); - if (m->deps) - freelinklist(m->deps, freestr); - zfree(m, sizeof(*m)); + modulestab->freenode(&m->node); } /* @@ -1473,12 +1674,11 @@ delete_module(LinkNode node) mod_export int module_loaded(const char *name) { - LinkNode node; Module m; - return ((node = find_module(name, FINDMOD_ALIASP, NULL)) && - (m = ((Module) getdata(node)))->u.handle && - !(m->flags & MOD_UNLOAD)); + return ((m = find_module(name, FINDMOD_ALIASP, NULL)) && + m->u.handle && + !(m->node.flags & MOD_UNLOAD)); } /* @@ -1575,7 +1775,7 @@ dyn_setup_module(Module m) if (fn) return fn(m); - zwarnnam(m->nam, "no setup function"); + zwarnnam(m->node.nam, "no setup function"); return 1; } @@ -1612,7 +1812,7 @@ dyn_boot_module(Module m) if(fn) return fn(m); - zwarnnam(m->nam, "no boot function"); + zwarnnam(m->node.nam, "no boot function"); return 1; } @@ -1624,7 +1824,7 @@ dyn_cleanup_module(Module m) if(fn) return fn(m); - zwarnnam(m->nam, "no cleanup function"); + zwarnnam(m->node.nam, "no cleanup function"); return 1; } @@ -1641,7 +1841,7 @@ dyn_finish_module(Module m) if (fn) r = fn(m); else { - zwarnnam(m->nam, "no finish function"); + zwarnnam(m->node.nam, "no finish function"); r = 1; } dlclose(m->u.handle); @@ -1655,7 +1855,7 @@ dyn_finish_module(Module m) static int setup_module(Module m) { - return ((m->flags & MOD_LINKED) ? + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->setup)(m) : dyn_setup_module(m)); } @@ -1663,7 +1863,7 @@ setup_module(Module m) static int features_module(Module m, char ***features) { - return ((m->flags & MOD_LINKED) ? + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->features)(m, features) : dyn_features_module(m, features)); } @@ -1672,7 +1872,7 @@ features_module(Module m, char ***features) static int enables_module(Module m, int **enables) { - return ((m->flags & MOD_LINKED) ? + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->enables)(m, enables) : dyn_enables_module(m, enables)); } @@ -1681,7 +1881,7 @@ enables_module(Module m, int **enables) static int boot_module(Module m) { - return ((m->flags & MOD_LINKED) ? + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->boot)(m) : dyn_boot_module(m)); } @@ -1689,7 +1889,7 @@ boot_module(Module m) static int cleanup_module(Module m) { - return ((m->flags & MOD_LINKED) ? + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->cleanup)(m) : dyn_cleanup_module(m)); } @@ -1697,7 +1897,7 @@ cleanup_module(Module m) static int finish_module(Module m) { - return ((m->flags & MOD_LINKED) ? + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->finish)(m) : dyn_finish_module(m)); } @@ -1708,14 +1908,14 @@ finish_module(Module m) static int setup_module(Module m) { - return ((m->flags & MOD_LINKED) ? (m->u.linked->setup)(m) : 1); + return ((m->node.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) + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->features)(m, features) : 1); } @@ -1723,7 +1923,7 @@ features_module(Module m, char ***features) static int enables_module(Module m, int **enables) { - return ((m->flags & MOD_LINKED) ? (m->u.linked->enables)(m, enables) + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->enables)(m, enables) : 1); } @@ -1731,21 +1931,21 @@ enables_module(Module m, int **enables) static int boot_module(Module m) { - return ((m->flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1); + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1); } /**/ static int cleanup_module(Module m) { - return ((m->flags & MOD_LINKED) ? (m->u.linked->cleanup)(m) : 1); + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->cleanup)(m) : 1); } /**/ static int finish_module(Module m) { - return ((m->flags & MOD_LINKED) ? (m->u.linked->finish)(m) : 1); + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->finish)(m) : 1); } /**/ @@ -1761,14 +1961,16 @@ finish_module(Module m) * by now (though may not be fully set up). * * Return 0 for success, 1 for failure, 2 if some features - * couldn't be set. + * couldn't be set by the module itself (non-existent features + * are tested here and cause 1 to be returned). */ /**/ static int -do_module_features(Module m, char **enablesstr, int silent) +do_module_features(Module m, char **enablesstr, int flags) { char **features; + int ret = 0; if (features_module(m, &features) == 0) { /* @@ -1781,12 +1983,62 @@ do_module_features(Module m, char **enablesstr, int silent) int *enables = NULL; if (enables_module(m, &enables)) { /* If features are supported, enables should be, too */ - if (!silent) + if (!(flags & FEAT_IGNORE)) zwarn("error getting enabled features for module `%s'", - m->nam); + m->node.nam); return 1; } + if ((flags & FEAT_CHECKAUTO) && m->autoloads) { + /* + * Check autoloads are available. Since these + * have been requested at some other point, they + * don't affect the return status unless something + * in enablesstr doesn't work. + */ + LinkNode an, nextn; + for (an = firstnode(m->autoloads); an; an = nextn) { + char *al = (char *)getdata(an), **ptr; + /* careful, we can delete the current node */ + nextn = nextnode(an); + for (ptr = features; *ptr; ptr++) + if (!strcmp(al, *ptr)) + break; + if (!*ptr) { + char *arg[2]; + if (!(flags & FEAT_IGNORE)) + zwarn( + "module `%s' has no such feature: `%s': autoload cancelled", + m->node.nam, al); + /* + * This shouldn't happen, so it's not worth optimising + * the call to autofeatures... + */ + arg[0] = al = dupstring(al); + arg[1] = NULL; + (void)autofeatures(NULL, m->node.nam, arg, 0, + FEAT_IGNORE|FEAT_REMOVE); + /* + * don't want to try to enable *that*... + * expunge it from the enable string. + */ + if (enablesstr) { + for (ptr = enablesstr; *ptr; ptr++) { + if (!strcmp(al, *ptr)) { + /* can't enable it after all, so return 1 */ + ret = 1; + while (*ptr) { + *ptr = ptr[1]; + ptr++; + } + break; + } + } + } + } + } + } + if (enablesstr) { char **ep; for (ep = enablesstr; *ep; ep++) { @@ -1804,9 +2056,9 @@ do_module_features(Module m, char **enablesstr, int silent) break; } if (!*fp) { - if (!silent) - zwarn("module `%s' has no such feature: %s", - m->nam, esp); + if (!(flags & FEAT_IGNORE)) + zwarn("module `%s' has no such feature: `%s'", + m->node.nam, esp); return 1; } } @@ -1824,13 +2076,13 @@ do_module_features(Module m, char **enablesstr, int silent) if (enables_module(m, &enables)) return 2; } else if (enablesstr) { - if (!silent) - zwarn("module `%s' does not support features", m->nam); + if (!(flags & FEAT_IGNORE)) + zwarn("module `%s' does not support features", m->node.nam); return 1; } /* Else it doesn't support features but we don't care. */ - return 0; + return ret; } /* @@ -1847,7 +2099,9 @@ do_module_features(Module m, char **enablesstr, int silent) static int do_boot_module(Module m, char **enablesstr, int silent) { - int ret = do_module_features(m, enablesstr, silent); + int ret = do_module_features(m, enablesstr, + silent ? FEAT_IGNORE|FEAT_CHECKAUTO : + FEAT_CHECKAUTO); if (ret == 1) return 1; @@ -1865,7 +2119,7 @@ do_boot_module(Module m, char **enablesstr, int silent) static int do_cleanup_module(Module m) { - return (m->flags & MOD_LINKED) ? + return (m->node.flags & MOD_LINKED) ? (m->u.linked && m->u.linked->cleanup(m)) : (m->u.handle && cleanup_module(m)); } @@ -1910,7 +2164,6 @@ load_module(char const *name, char **enablesstr, int silent) Module m; void *handle = NULL; Linkedmod linked; - LinkNode node, n; int set, bootret; if (!modname_ok(name)) { @@ -1924,66 +2177,65 @@ load_module(char const *name, char **enablesstr, int silent) * is the right one. */ queue_signals(); - if (!(node = find_module(name, FINDMOD_ALIASP, &name))) { + if (!(m = find_module(name, FINDMOD_ALIASP, &name))) { if (!(linked = module_linked(name)) && !(handle = do_load_module(name, silent))) { unqueue_signals(); return 1; } m = zshcalloc(sizeof(*m)); - m->nam = ztrdup(name); if (handle) { m->u.handle = handle; - m->flags |= MOD_SETUP; + m->node.flags |= MOD_SETUP; } else { m->u.linked = linked; - m->flags |= MOD_SETUP | MOD_LINKED; + m->node.flags |= MOD_SETUP | MOD_LINKED; } - node = zaddlinknode(modules, m); + modulestab->addnode(modulestab, ztrdup(name), m); if ((set = setup_module(m)) || (bootret = do_boot_module(m, enablesstr, silent)) == 1) { - if (!set) { + if (!set) do_cleanup_module(m); - finish_module(m); - } - delete_module(node); + finish_module(m); + delete_module(m); unqueue_signals(); return 1; } - m->flags |= MOD_INIT_S | MOD_INIT_B; - m->flags &= ~MOD_SETUP; + m->node.flags |= MOD_INIT_S | MOD_INIT_B; + m->node.flags &= ~MOD_SETUP; unqueue_signals(); return bootret; } - m = (Module) getdata(node); - if (m->flags & MOD_SETUP) { + if (m->node.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) { + if (m->node.flags & MOD_UNLOAD) + m->node.flags &= ~MOD_UNLOAD; + else if ((m->node.flags & MOD_LINKED) ? m->u.linked : m->u.handle) { unqueue_signals(); return 0; } - if (m->flags & MOD_BUSY) { - zerr("circular dependencies for module %s", name); + if (m->node.flags & MOD_BUSY) { + zerr("circular dependencies for module ;%s", name); return 1; } - m->flags |= MOD_BUSY; + m->node.flags |= MOD_BUSY; /* * TODO: shouldn't we unload the module if one of * its dependencies fails? */ - if (m->deps) + if (m->deps) { + LinkNode n; for (n = firstnode(m->deps); n; incnode(n)) if (load_module((char *) getdata(n), NULL, silent) == 1) { - m->flags &= ~MOD_BUSY; + m->node.flags &= ~MOD_BUSY; unqueue_signals(); return 1; } - m->flags &= ~MOD_BUSY; + } + m->node.flags &= ~MOD_BUSY; if (!m->u.handle) { handle = NULL; if (!(linked = module_linked(name)) && @@ -1993,36 +2245,37 @@ load_module(char const *name, char **enablesstr, int silent) } if (handle) { m->u.handle = handle; - m->flags |= MOD_SETUP; + m->node.flags |= MOD_SETUP; } else { m->u.linked = linked; - m->flags |= MOD_SETUP | MOD_LINKED; + m->node.flags |= MOD_SETUP | MOD_LINKED; } if (setup_module(m)) { + finish_module(m); if (handle) m->u.handle = NULL; else m->u.linked = NULL; - m->flags &= ~MOD_SETUP; + m->node.flags &= ~MOD_SETUP; unqueue_signals(); return 1; } - m->flags |= MOD_INIT_S; + m->node.flags |= MOD_INIT_S; } - m->flags |= MOD_SETUP; + m->node.flags |= MOD_SETUP; if ((bootret = do_boot_module(m, enablesstr, silent)) == 1) { do_cleanup_module(m); finish_module(m); - if (m->flags & MOD_LINKED) + if (m->node.flags & MOD_LINKED) m->u.linked = NULL; else m->u.handle = NULL; - m->flags &= ~MOD_SETUP; + m->node.flags &= ~MOD_SETUP; unqueue_signals(); return 1; } - m->flags |= MOD_INIT_B; - m->flags &= ~MOD_SETUP; + m->node.flags |= MOD_INIT_B; + m->node.flags &= ~MOD_SETUP; unqueue_signals(); return bootret; } @@ -2030,7 +2283,8 @@ load_module(char const *name, char **enablesstr, int silent) /* This ensures that the module with the name given as the first argument * is loaded. * The other argument is the array of features to set. If this is NULL - * and the module needs to be loaded, all features are enabled. + * all features are enabled (even if the module was already loaded). + * * If this is non-NULL the module features are set accordingly * whether or not the module is loaded; it is an error if the * module does not support the features passed (even if the feature @@ -2050,14 +2304,13 @@ mod_export int require_module(const char *module, char **features) { 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)) + m = find_module(module, FINDMOD_ALIASP, &module); + if (!m || !m->u.handle || + (m->node.flags & MOD_UNLOAD)) ret = load_module(module, features, 0); else ret = do_module_features(m, features, 0); @@ -2090,7 +2343,7 @@ add_dep(const char *name, char *from) * Better make sure. (There's no problem making a an alias which * *points* to a module with dependencies, of course.) */ - m = getdata(find_module(name, FINDMOD_ALIASP|FINDMOD_CREATE, &name)); + m = find_module(name, FINDMOD_ALIASP|FINDMOD_CREATE, &name); if (!m->deps) m->deps = znewlinklist(); for (node = firstnode(m->deps); @@ -2179,7 +2432,8 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) 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,'a') && !OPT_ISSET(ops,'F')) + || OPT_ISSET(ops,'d') || OPT_ISSET(ops,'i') || OPT_ISSET(ops,'u'))) { zwarnnam(nam, "-e cannot be combined with other options"); /* except -F ... */ @@ -2233,7 +2487,6 @@ bin_zmodload_alias(char *nam, char **args, Options ops) * suppose other names are aliased to the same file? It might be * kettle of fish best left unwormed. */ - LinkNode node; Module m; if (!*args) { @@ -2241,11 +2494,9 @@ bin_zmodload_alias(char *nam, char **args, Options ops) 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); - } + scanhashtable(modulestab, 1, MOD_ALIAS, 0, + modulestab->printnode, + OPT_ISSET(ops,'L') ? PRINTMOD_LIST : 0); return 0; } @@ -2264,14 +2515,13 @@ bin_zmodload_alias(char *nam, char **args, Options ops) *args); return 1; } - node = find_module(*args, 0, NULL); - if (node) { - m = (Module) getdata(node); - if (!(m->flags & MOD_ALIAS)) { + m = find_module(*args, 0, NULL); + if (m) { + if (!(m->node.flags & MOD_ALIAS)) { zwarnnam(nam, "module is not an alias: %s", *args); return 1; } - delete_module(node); + delete_module(m); } else { zwarnnam(nam, "no such module alias: %s", *args); return 1; @@ -2289,29 +2539,28 @@ bin_zmodload_alias(char *nam, char **args, Options ops) *args); return 1; } - } while ((node = find_module(mname, 0, NULL)) - && ((m = (Module) getdata(node))->flags & MOD_ALIAS) + } while ((m = find_module(mname, 0, NULL)) + && (m->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)) { + m = find_module(*args, 0, NULL); + if (m) { + if (!(m->node.flags & MOD_ALIAS)) { zwarnnam(nam, "module is not an alias: %s", *args); return 1; } zsfree(m->u.alias); } else { m = (Module) zshcalloc(sizeof(*m)); - m->nam = ztrdup(*args); - m->flags = MOD_ALIAS; - zaddlinknode(modules, m); + m->node.flags = MOD_ALIAS; + modulestab->addnode(modulestab, ztrdup(*args), 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); + if ((m = find_module(*args, 0, NULL))) { + if (m->node.flags & MOD_ALIAS) + modulestab->printnode(&m->node, + OPT_ISSET(ops,'L') ? + PRINTMOD_LIST : 0); else { zwarnnam(nam, "module is not an alias: %s", *args); return 1; @@ -2333,35 +2582,20 @@ bin_zmodload_alias(char *nam, char **args, Options ops) static int bin_zmodload_exist(UNUSED(char *nam), char **args, Options ops) { - LinkNode node; Module m; - char *modname; if (!*args) { - for (node = firstnode(modules); node; incnode(node)) { - m = (Module) getdata(node); - modname = m->nam; - if (m->flags & MOD_ALIAS) { - LinkNode node2; - if (OPT_ISSET(ops,'A') && - (node2 = find_module(m->u.alias, FINDMOD_ALIASP, NULL))) - m = (Module) getdata(node2); - else - continue; - } - if (m->u.handle && !(m->flags & MOD_UNLOAD)) { - nicezputs(modname, stdout); - putchar('\n'); - } - } + scanhashtable(modulestab, 1, 0, 0, modulestab->printnode, + OPT_ISSET(ops,'A') ? PRINTMOD_EXIST|PRINTMOD_ALIAS : + PRINTMOD_EXIST); return 0; } else { int ret = 0; for (; !ret && *args; args++) { - if (!(node = find_module(*args, FINDMOD_ALIASP, NULL)) - || !(m = (Module) getdata(node))->u.handle - || (m->flags & MOD_UNLOAD)) + if (!(m = find_module(*args, FINDMOD_ALIASP, NULL)) + || !m->u.handle + || (m->node.flags & MOD_UNLOAD)) ret = 1; } return ret; @@ -2374,15 +2608,13 @@ bin_zmodload_exist(UNUSED(char *nam), char **args, Options ops) static int bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops) { - LinkNode node; Module m; if (OPT_ISSET(ops,'u')) { /* remove dependencies, which can't pertain to aliases */ const char *tnam = *args++; - node = find_module(tnam, FINDMOD_ALIASP, &tnam); - if (!node) + m = find_module(tnam, FINDMOD_ALIASP, &tnam); + if (!m) return 0; - m = (Module) getdata(node); if (*args && m->deps) { do { LinkNode dnode; @@ -2404,32 +2636,18 @@ bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops) } } if (!m->deps && !m->u.handle) - delete_module(node); + delete_module(m); 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'); - } + int depflags = OPT_ISSET(ops,'L') ? + PRINTMOD_DEPS|PRINTMOD_LIST : PRINTMOD_DEPS; + if (args[0]) { + if ((m = (Module)modulestab->getnode2(modulestab, args[0]))) + modulestab->printnode(&m->node, depflags); + } else { + scanhashtable(modulestab, 1, 0, 0, modulestab->printnode, + depflags); } return 0; } else { @@ -2467,7 +2685,7 @@ printautoparams(HashNode hn, int lon) static int bin_zmodload_auto(char *nam, char **args, Options ops) { - int fchar; + int fchar, flags; char *modnam; if (OPT_ISSET(ops,'c')) { @@ -2530,9 +2748,12 @@ bin_zmodload_auto(char *nam, char **args, Options ops) fchar = 'b'; } + flags = FEAT_AUTOALL; + if (OPT_ISSET(ops,'i')) + flags |= FEAT_IGNORE; if (OPT_ISSET(ops,'u')) { /* remove autoload */ - fchar *= -1; + flags |= FEAT_REMOVE; modnam = NULL; } else { /* add autoload */ @@ -2541,94 +2762,113 @@ bin_zmodload_auto(char *nam, char **args, Options ops) if (args[1]) args++; } - return autofeatures(nam, modnam, args, fchar, OPT_ISSET(ops,'i')); + return autofeatures(nam, modnam, args, fchar, flags); } /* Backend handler for zmodload -u */ /**/ int -unload_module(Module m, LinkNode node) +unload_module(Module m) { + int del; + /* * Only unload the real module, so resolve aliases. */ - if (m->flags & MOD_ALIAS) { - LinkNode node = find_module(m->u.alias, FINDMOD_ALIASP, NULL); - if (!node) + if (m->node.flags & MOD_ALIAS) { + m = find_module(m->u.alias, FINDMOD_ALIASP, NULL); + if (!m) return 1; - m = (Module) getdata(node); } - if ((m->flags & MOD_INIT_S) && - !(m->flags & MOD_UNLOAD) && + /* + * We may need to clean up the module any time setup_ has been + * called. After cleanup_ is successful we are no longer in the + * booted state (because features etc. are deregistered), so remove + * MOD_INIT_B, and also MOD_INIT_S since we won't need to cleanup + * again if this succeeded. + */ + if ((m->node.flags & MOD_INIT_S) && + !(m->node.flags & MOD_UNLOAD) && do_cleanup_module(m)) return 1; - else { - int del = (m->flags & MOD_UNLOAD); + m->node.flags &= ~(MOD_INIT_B|MOD_INIT_S); - if (m->wrapper) { - m->flags |= MOD_UNLOAD; - return 0; + del = (m->node.flags & MOD_UNLOAD); + + if (m->wrapper) { + m->node.flags |= MOD_UNLOAD; + return 0; + } + m->node.flags &= ~MOD_UNLOAD; + + /* + * We always need to finish the module (and unload it) + * if it is present. + */ + if (m->node.flags & MOD_LINKED) { + if (m->u.linked) { + m->u.linked->finish(m); + m->u.linked = NULL; } - 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; - } - } + } 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), - FINDMOD_ALIASP, NULL); - Module dm; - - if (dn && (dm = (Module) getdata(dn)) && - (dm->flags & MOD_UNLOAD)) { - /* See if this is the only module depending on it. */ - - LinkNode an; - Module am; - int du = 1; - - for (an = firstnode(modules); du && an; incnode(an)) { - am = (Module) getdata(an); - if (am != m && am->deps && - ((am->flags & MOD_LINKED) ? - am->u.linked : am->u.handle)) { - LinkNode sn; - - for (sn = firstnode(am->deps); du && sn; - incnode(sn)) { - if (!strcmp((char *) getdata(sn), dm->nam)) - du = 0; - } + } + + if (del && m->deps) { + /* The module was unloaded delayed, unload all modules * + * on which it depended. */ + LinkNode n; + + for (n = firstnode(m->deps); n; incnode(n)) { + Module dm = find_module((char *) getdata(n), + FINDMOD_ALIASP, NULL); + + if (dm && + (dm->node.flags & MOD_UNLOAD)) { + /* See if this is the only module depending on it. */ + Module am; + int du = 1, i; + /* Scan hash table the hard way */ + for (i = 0; du && i < modulestab->hsize; i++) { + for (am = (Module)modulestab->nodes[i]; du && am; + am = (Module)am->node.next) { + LinkNode sn; + /* + * Don't scan the module we're unloading; + * ignore if no dependencies. + */ + if (am == m || !am->deps) + continue; + /* Don't scan if not loaded nor linked */ + if ((am->node.flags & MOD_LINKED) ? + !am->u.linked : !am->u.handle) + continue; + for (sn = firstnode(am->deps); du && sn; + incnode(sn)) { + if (!strcmp((char *) getdata(sn), + dm->node.nam)) + du = 0; } } - if (du) - unload_module(dm, NULL); } + if (du) + unload_module(dm); } } - if(!m->deps) { - if (!node) { - node = linknodebydatum(modules, m); - if (!node) - return 1; - } - delete_module(node); - } + } + if (m->autoloads && firstnode(m->autoloads)) { + /* + * Module has autoloadable features. Restore them + * so that the module will be reloaded when needed. + */ + autofeatures("zsh", m->node.nam, + hlinklist2array(m->autoloads, 0), 0, FEAT_IGNORE); + } else if (!m->deps) { + delete_module(m); } return 0; } @@ -2644,32 +2884,35 @@ int unload_named_module(char *modname, char *nam, int silent) { const char *mname; - LinkNode node; Module m; int ret = 0; - node = find_module(modname, FINDMOD_ALIASP, &mname); - if (node) { - LinkNode mn, dn; - int del = 0; + m = find_module(modname, FINDMOD_ALIASP, &mname); + if (m) { + int i, del = 0; + Module dm; - 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)) + for (i = 0; i < modulestab->hsize; i++) { + for (dm = (Module)modulestab->nodes[i]; dm; + dm = (Module)dm->node.next) { + LinkNode dn; + if (!dm->deps || !dm->u.handle) + continue; + for (dn = firstnode(dm->deps); dn; incnode(dn)) { if (!strcmp((char *) getdata(dn), mname)) { - if (m->flags & MOD_UNLOAD) + if (dm->node.flags & MOD_UNLOAD) del = 1; else { zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", mname); return 1; } } + } + } } - m = (Module) getdata(node); if (del) m->wrapper++; - if (unload_module(m, node)) + if (unload_module(m)) ret = 1; if (del) m->wrapper--; @@ -2687,8 +2930,6 @@ unload_named_module(char *modname, char *nam, int silent) static int bin_zmodload_load(char *nam, char **args, Options ops) { - LinkNode node; - Module m; int ret = 0; if(OPT_ISSET(ops,'u')) { /* unload modules */ @@ -2699,19 +2940,9 @@ bin_zmodload_load(char *nam, char **args, Options ops) 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'); - } - } + scanhashtable(modulestab, 1, 0, MOD_UNLOAD|MOD_ALIAS, + modulestab->printnode, + OPT_ISSET(ops,'L') ? PRINTMOD_LIST : 0); return 0; } else { /* load modules */ @@ -2729,15 +2960,31 @@ bin_zmodload_load(char *nam, char **args, Options ops) /**/ static int -bin_zmodload_features(char *nam, char **args, Options ops) +bin_zmodload_features(const char *nam, char **args, Options ops) { char *modname = *args; + if (modname) + args++; + else if (OPT_ISSET(ops,'L')) { + int printflags = PRINTMOD_LIST|PRINTMOD_FEATURES; + if (OPT_ISSET(ops,'P')) { + zwarnnam(nam, "-P is only allowed with a module name"); + return 1; + } + if (OPT_ISSET(ops,'l')) + printflags |= PRINTMOD_LISTALL; + if (OPT_ISSET(ops,'a')) + printflags |= PRINTMOD_AUTO; + scanhashtable(modulestab, 1, 0, MOD_ALIAS, + modulestab->printnode, printflags); + return 0; + } + if (!modname) { zwarnnam(nam, "-F requires a module name"); return 1; } - args++; if (OPT_ISSET(ops,'l') || OPT_ISSET(ops,'L') || OPT_ISSET(ops,'e')) { /* @@ -2746,32 +2993,74 @@ bin_zmodload_features(char *nam, char **args, Options ops) * only options turned on. * With both options, list as zmodload showing options * to be turned both on and off. - * - * TODO: handle -a, list only autoloads. */ - LinkNode node; - Module m = NULL; + Module m; char **features, **fp, **arrset = NULL, **arrp = NULL; int *enables = NULL, *ep; char *param = OPT_ARG_SAFE(ops,'P'); - node = find_module(modname, FINDMOD_ALIASP, NULL); - if (node) - m = ((Module) getdata(node)); - if (!m || !m->u.handle || (m->flags & MOD_UNLOAD)) { + m = find_module(modname, FINDMOD_ALIASP, NULL); + if (OPT_ISSET(ops,'a')) { + LinkNode ln; + /* + * If there are no autoloads defined, return status 1. + */ + if (!m || !m->autoloads) + return 1; + if (OPT_ISSET(ops,'e')) { + for (fp = args; *fp; fp++) { + char *fstr = *fp; + int sense = 1; + if (*fstr == '+') + fstr++; + else if (*fstr == '-') { + fstr++; + sense = 0; + } + if ((linknodebystring(m->autoloads, fstr) != NULL) != + sense) + return 1; + } + return 0; + } + if (param) { + arrp = arrset = (char **)zalloc(sizeof(char*) * + (countlinknodes(m->autoloads)+1)); + } else if (OPT_ISSET(ops,'L')) { + printf("zmodload -aF %s%c", m->node.nam, + m->autoloads && firstnode(m->autoloads) ? ' ' : '\n'); + arrp = NULL; + } + for (ln = firstnode(m->autoloads); ln; incnode(ln)) { + char *al = (char *)getdata(ln); + if (param) + *arrp++ = ztrdup(al); + else + printf("%s%c", al, + OPT_ISSET(ops,'L') && nextnode(ln) ? ' ' : '\n'); + } + if (param) { + *arrp = NULL; + if (!setaparam(param, arrset)) + return 1; + } + return 0; + } + if (!m || !m->u.handle || (m->node.flags & MOD_UNLOAD)) { if (!OPT_ISSET(ops,'e')) zwarnnam(nam, "module `%s' is not yet loaded", modname); return 1; } if (features_module(m, &features)) { if (!OPT_ISSET(ops,'e')) - zwarnnam(nam, "module `%s' does not support features", m->nam); + zwarnnam(nam, "module `%s' does not support features", + m->node.nam); return 1; } if (enables_module(m, &enables)) { /* this shouldn't ever happen, so don't silence this error */ zwarnnam(nam, "error getting enabled features for module `%s'", - m->nam); + m->node.nam); return 1; } for (arrp = args; *arrp; arrp++) { @@ -2796,14 +3085,14 @@ bin_zmodload_features(char *nam, char **args, Options ops) } if (!*fp) { if (!OPT_ISSET(ops,'e')) - zwarnnam(nam, "module `%s' has no such feature: %s", + 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')) { + if (param) { int arrlen = 0; for (fp = features, ep = enables; *fp; fp++, ep++) { if (OPT_ISSET(ops, 'L') && !OPT_ISSET(ops, 'l') && @@ -2826,7 +3115,7 @@ bin_zmodload_features(char *nam, char **args, Options ops) } arrp = arrset = zalloc(sizeof(char *) * (arrlen+1)); } else if (OPT_ISSET(ops, 'L')) - printf("zmodload -F %s ", m->nam); + printf("zmodload -F %s ", m->node.nam); for (fp = features, ep = enables; *fp; fp++, ep++) { char *onoff; int term; @@ -2872,7 +3161,18 @@ bin_zmodload_features(char *nam, char **args, Options ops) zwarnnam(nam, "-P can only be used with -l or -L"); return 1; } else if (OPT_ISSET(ops,'a')) { - return autofeatures(nam, modname, args, 0, OPT_ISSET(ops,'i')); + /* + * With zmodload -aF, we always use the effect of -i. + * The thinking is that marking a feature for + * autoload is separate from enabling or disabling it. + * Arguably we could do this with the zmodload -ab method + * but I've kept it there for old time's sake. + * The decoupling has meant FEAT_IGNORE/-i also + * suppresses an error for attempting to remove an + * autoload when the feature is enabled, which used + * to be a hard error before. + */ + return autofeatures(nam, modname, args, 0, FEAT_IGNORE); } return require_module(modname, args); @@ -2894,15 +3194,15 @@ bin_zmodload_features(char *nam, char **args, Options ops) /**/ mod_export char ** -featuresarray(char const *nam, Features f) +featuresarray(UNUSED(Module m), Features f) { int bn_size = f->bn_size, cd_size = f->cd_size; - int pd_size = f->pd_size, mf_size = f->mf_size; + int mf_size = f->mf_size, pd_size = f->pd_size; int features_size = bn_size + cd_size + pd_size + mf_size + f->n_abstract; Builtin bnp = f->bn_list; Conddef cdp = f->cd_list; - Paramdef pdp = f->pd_list; MathFunc mfp = f->mf_list; + Paramdef pdp = f->pd_list; char **features = (char **)zhalloc((features_size + 1) * sizeof(char *)); char **featurep = features; @@ -2913,10 +3213,10 @@ featuresarray(char const *nam, Features f) cdp->name); cdp++; } - while (pd_size--) - *featurep++ = dyncat("p:", (pdp++)->name); while (mf_size--) *featurep++ = dyncat("f:", (mfp++)->name); + while (pd_size--) + *featurep++ = dyncat("p:", (pdp++)->name); features[features_size] = NULL; return features; @@ -2929,15 +3229,15 @@ featuresarray(char const *nam, Features f) */ /**/ mod_export int * -getfeatureenables(char const *nam, Features f) +getfeatureenables(UNUSED(Module m), Features 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; + int mf_size = f->mf_size, pd_size = f->pd_size; + int features_size = bn_size + cd_size + mf_size + pd_size + f->n_abstract; Builtin bnp = f->bn_list; Conddef cdp = f->cd_list; - Paramdef pdp = f->pd_list; MathFunc mfp = f->mf_list; + Paramdef pdp = f->pd_list; int *enables = zhalloc(sizeof(int) * features_size); int *enablep = enables; @@ -2945,10 +3245,10 @@ getfeatureenables(char const *nam, Features f) *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; + while (pd_size--) + *enablep++ = (pdp++)->pm ? 1 : 0; return enables; } @@ -2965,32 +3265,32 @@ getfeatureenables(char const *nam, Features f) /**/ mod_export int -setfeatureenables(char const *nam, Features f, int *e) +setfeatureenables(Module m, Features f, int *e) { int ret = 0; if (f->bn_size) { - if (setbuiltins(nam, f->bn_list, f->bn_size, e)) + if (setbuiltins(m->node.nam, f->bn_list, f->bn_size, e)) ret = 1; if (e) e += f->bn_size; } if (f->cd_size) { - if (setconddefs(nam, f->cd_list, f->cd_size, e)) + if (setconddefs(m->node.nam, f->cd_list, f->cd_size, e)) ret = 1; if (e) e += f->cd_size; } + if (f->mf_size) { + if (setmathfuncs(m->node.nam, f->mf_list, f->mf_size, e)) + ret = 1; + } if (f->pd_size) { - if (setparamdefs(nam, f->pd_list, f->pd_size, e)) + if (setparamdefs(m->node.nam, f->pd_list, f->pd_size, e)) ret = 1; if (e) e += f->pd_size; } - if (f->mf_size) { - if (setmathfuncs(nam, f->mf_list, f->mf_size, e)) - ret = 1; - } return ret; } @@ -3001,31 +3301,40 @@ setfeatureenables(char const *nam, Features f, int *e) /**/ mod_export int -handlefeatures(char *nam, Features f, int **enables) +handlefeatures(Module m, Features f, int **enables) { if (!enables || *enables) - return setfeatureenables(nam, f, *enables); - *enables = getfeatureenables(nam, f); + return setfeatureenables(m, f, *enables); + *enables = getfeatureenables(m, f); return 0; } /* * Ensure module "modname" is providing feature with "prefix" - * and "feature" (e.g. "b:", "limit"). + * and "feature" (e.g. "b:", "limit"). If feature is NULL, + * ensure all features are loaded (used for compatibility + * with the pre-feature autoloading behaviour). * * This will usually be called from the main shell to handle * loading of an autoloadable feature. * * Returns 0 on success, 1 for error in module, 2 for error - * setting the feature. + * setting the feature. However, this isn't actually all + * that useful for testing immediately on an autoload since + * it could be a failure to autoload a different feature + * from the one we want. We could fix this but it's + * possible to test other ways. */ /**/ mod_export int -ensurefeature(char *modname, char *prefix, char *feature) +ensurefeature(const char *modname, const char *prefix, const char *feature) { - char *f = dyncat(prefix, feature); - char *features[2]; + char *f, *features[2]; + + if (!feature) + return require_module(modname, NULL); + f = dyncat(prefix, feature); features[0] = f; features[1] = NULL; @@ -3038,28 +3347,38 @@ ensurefeature(char *modname, char *prefix, char *feature) /**/ int -autofeatures(char *cmdnam, char *module, char **features, int prefchar, - int opt_i) +autofeatures(const char *cmdnam, const char *module, char **features, + int prefchar, int defflags) { int ret = 0, subret; - int defflags = opt_i ? AUTOFEAT_IGNORE : 0; + Module defm, m; + char **modfeatures = NULL; + if (module) { + defm = (Module)find_module(module, + FINDMOD_ALIASP|FINDMOD_CREATE, NULL); + if ((defm->node.flags & MOD_LINKED) ? defm->u.linked : + defm->u.handle) + (void)features_module(defm, &modfeatures); + } else + defm = NULL; - while (*features) { - char *fnam, *typnam; + for (; *features; features++) { + char *fnam, *typnam, *feature; int add, fchar, flags = defflags; autofeaturefn_t fn; if (prefchar) { - if (prefchar < 0) { - add = 0; - fchar = - prefchar; - } else { - add = 1; - fchar = prefchar; - } + /* + * "features" is list of bare features with no + * type prefix; prefchar gives type character. + */ + add = 1; /* unless overridden by flag */ + fchar = prefchar; fnam = *features; + feature = zhalloc(strlen(fnam) + 3); + sprintf(feature, "%c:%s", fchar, fnam); } else { - char *feature = *features; + feature = *features; if (*feature == '-') { add = 0; feature++; @@ -3073,12 +3392,13 @@ autofeatures(char *cmdnam, char *module, char **features, int prefchar, zwarnnam(cmdnam, "bad format for autoloadable feature: `%s'", feature); ret = 1; + continue; } fnam = feature + 2; fchar = feature[0]; } - - features++; + if (flags & FEAT_REMOVE) + add = 0; switch (fchar) { case 'b': @@ -3087,23 +3407,23 @@ autofeatures(char *cmdnam, char *module, char **features, int prefchar, break; case 'C': - flags |= AUTOFEAT_INFIX; + flags |= FEAT_INFIX; /* FALLTHROUGH */ case 'c': fn = add ? add_autocond : del_autocond; typnam = "condition"; break; - case 'p': - fn = add ? add_autoparam : del_autoparam; - typnam = "parameter"; - break; - case 'f': fn = add ? add_automathfunc : del_automathfunc; typnam = "math function"; break; + case 'p': + fn = add ? add_autoparam : del_autoparam; + typnam = "parameter"; + break; + default: zwarnnam(cmdnam, "bad autoloadable feature type: `%c'", fchar); @@ -3116,10 +3436,91 @@ autofeatures(char *cmdnam, char *module, char **features, int prefchar, ret = 1; continue; } - subret = fn(module, fnam, flags); + + if (!module) { + /* + * Traditional un-autoload syntax doesn't tell us + * which module this came from. + */ + int i; + for (i = 0, m = NULL; !m && i < modulestab->hsize; i++) { + for (m = (Module)modulestab->nodes[i]; m; + m = (Module)m->node.next) { + if (m->autoloads && + linknodebystring(m->autoloads, feature)) + break; + } + } + if (!m) { + if (!(flags & FEAT_IGNORE)) { + ret = 1; + zwarnnam(cmdnam, "%s: no such %s", fnam, typnam); + } + continue; + } + } else + m = defm; + + subret = 0; + if (add) { + char **ptr; + if (modfeatures) { + /* + * If the module is already available, check that + * it does in fact provide the necessary feature. + */ + for (ptr = modfeatures; *ptr; ptr++) + if (!strcmp(*ptr, feature)) + break; + if (!*ptr) { + zwarnnam(cmdnam, "module `%s' has no such feature: `%s'", + m->node.nam, feature); + ret = 1; + continue; + } + } + if (!m->autoloads) { + m->autoloads = znewlinklist(); + zaddlinknode(m->autoloads, ztrdup(feature)); + } else { + /* Insert in lexical order */ + LinkNode ln, prev = (LinkNode)m->autoloads; + while ((ln = nextnode(prev))) { + int cmp = strcmp(feature, (char *)getdata(ln)); + if (cmp == 0) { + /* Already there. Never an error. */ + break; + } + if (cmp < 0) { + zinsertlinknode(m->autoloads, prev, + ztrdup(feature)); + break; + } + prev = ln; + } + if (!ln) + zaddlinknode(m->autoloads, ztrdup(feature)); + } + } else if (m->autoloads) { + LinkNode ln; + if ((ln = linknodebystring(m->autoloads, feature))) + zsfree((char *)remnode(m->autoloads, ln)); + else { + /* + * With -i (or zmodload -Fa), removing an autoload + * that's not there is not an error. + */ + subret = (flags & FEAT_IGNORE) ? -2 : 2; + } + } + + if (subret == 0) + subret = fn(module, fnam, flags); if (subret != 0) { - ret = 1; + /* -2 indicates not an error, just skip running fn() */ + if (subret != -2) + ret = 1; switch (subret) { case 1: zwarnnam(cmdnam, "failed to add %s `%s'", typnam, fnam); |