summaryrefslogtreecommitdiff
path: root/Src/module.c
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2007-05-28 22:57:39 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2007-05-28 22:57:39 +0000
commitb0c5f09169ac31855ebf0e93772bb57b9635b380 (patch)
tree410c43a9843b2c88166c2cb9acd531eaa36d036d /Src/module.c
parentbd7632079045b1b6d0dee498c40833b409cf757e (diff)
downloadzsh-b0c5f09169ac31855ebf0e93772bb57b9635b380.tar.gz
zsh-b0c5f09169ac31855ebf0e93772bb57b9635b380.zip
see 23479: add initial features support for modules
Diffstat (limited to 'Src/module.c')
-rw-r--r--Src/module.c1983
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;
}