diff -r 000000000000 -r 2e3d3ce01487 openenvutils/commandshell/shell/src/module.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openenvutils/commandshell/shell/src/module.c Tue Feb 02 10:12:00 2010 +0200 @@ -0,0 +1,2227 @@ +// module.c - deal with dynamic modules +// +// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. +// +/* + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1996-1997 Zoltán Hidvégi + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Zoltán Hidvégi or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Zoltán Hidvégi and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Zoltán Hidvégi and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Zoltán Hidvégi and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ +#include "zsh.mdh" +#include "module.pro" + +#ifdef __SYMBIAN32__ +#ifdef __WINSCW__ +#pragma warn_unusedarg off +#pragma warn_possunwant off +#endif//__WINSCW__ +#endif//__SYMBIAN32__ + +/* List of linked-in modules. */ + +/**/ +LinkList linkedmodules; + + +/* The `zsh/main' module contains all the base code that can't actually be * + * built as a separate module. It is initialised by main(), so there's * + * nothing for the boot function to do. */ + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +boot_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +cleanup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} + +/* This registers a builtin module. */ + +/**/ +void +register_module(char *n, Module_func setup, Module_func boot, + Module_func cleanup, Module_func finish) +{ + Linkedmod m; + + m = (Linkedmod) zalloc(sizeof(*m)); + + m->name = ztrdup(n); + m->setup = setup; + m->boot = boot; + m->cleanup = cleanup; + m->finish = finish; + + 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. */ + +/**/ +Linkedmod +module_linked(char const *name) +{ + LinkNode node; + + for (node = firstnode(linkedmodules); node; incnode(node)) + if (!strcmp(((Linkedmod) getdata(node))->name, name)) + return (Linkedmod) getdata(node); + + return NULL; +} + +/* addbuiltin() can be used to add a new builtin. It returns zero on * + * success, 1 on failure. The only possible type of failure is that * + * a builtin with the specified name already exists. An autoloaded * + * builtin can be replaced using this function. */ + +/**/ +int +addbuiltin(Builtin b) +{ + Builtin bn = (Builtin) builtintab->getnode2(builtintab, b->nam); + if (bn && (bn->flags & BINF_ADDED)) + return 1; + if (bn) + builtintab->freenode(builtintab->removenode(builtintab, b->nam)); + builtintab->addnode(builtintab, b->nam, 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)); */ + +/**/ +mod_export int +addbuiltins(char const *nam, Builtin binl, int size) +{ + int hads = 0, hadf = 0, n; + + for(n = 0; n < size; n++) { + Builtin b = &binl[n]; + if(b->flags & BINF_ADDED) + continue; + if(addbuiltin(b)) { + zwarnnam(nam, "name clash when adding builtin `%s'", b->nam, 0); + hadf = 1; + } else { + b->flags |= BINF_ADDED; + hads = 2; + } + } + return hadf ? hads : 1; +} + +/* The list of function wrappers defined. */ + +/**/ +FuncWrap wrappers; + +/* This adds a definition for a wrapper. Return value is one in case of * + * error and zero if all went fine. */ + +/**/ +mod_export int +addwrapper(Module m, FuncWrap w) +{ + FuncWrap p, q; + + /* + * We can't add a wrapper to an alias, since it's supposed + * to behave identically to the resolved module. This shouldn't + * happen since we usually add wrappers when a real module is + * loaded. + */ + if (m->flags & MOD_ALIAS) + return 1; + + if (w->flags & WRAPF_ADDED) + return 1; + for (p = wrappers, q = NULL; p; q = p, p = p->next); + if (q) + q->next = w; + else + wrappers = w; + w->next = NULL; + w->flags |= WRAPF_ADDED; + w->module = m; + + return 0; +} + +/* $module_path ($MODULE_PATH) */ + +/**/ +char **module_path; + +/* List of modules */ + +/**/ +mod_export LinkList modules; + +/* Define an autoloadable builtin. It returns 0 on success, or 1 on * + * failure. The only possible cause of failure is that a builtin * + * with the specified name already exists. */ + +/**/ +int +add_autobin(char *nam, char *module) +{ + Builtin bn = zshcalloc(sizeof(*bn)); + bn->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((HashNode)bn); + return 0; +} + +/* Delete multiple builtins. binl points to a table of `size' builtin * + * structures. Those for which (.flags & BINF_ADDED) is true are to be * + * deleted; that flag is cleared. If any fail, an error message is * + * printed, using nam as the leading name. Returns 1 if all deletions * + * succeed, 2 if some succeed and some fail, and 0 if all (and at least * + * 1) fail. In normal use, from a cleanup_*() function, this return * + * value would be ignored -- the only cause of failure would be that a * + * wayward module had deleted our builtin without telling us. */ + +/**/ +mod_export int +deletebuiltins(char const *nam, Builtin binl, int size) +{ + int hads = 0, hadf = 0, n; + + for(n = 0; n < size; n++) { + Builtin b = &binl[n]; + if(!(b->flags & BINF_ADDED)) + continue; + if(deletebuiltin(b->nam)) { + zwarnnam(nam, "builtin `%s' already deleted", b->nam, 0); + hadf = 1; + } else + hads = 2; + b->flags &= ~BINF_ADDED; + } + return hadf ? hads : 1; +} + +/* This removes the given wrapper definition from the list. Returned is * + * one in case of error and zero otherwise. */ + +/**/ +mod_export int +deletewrapper(Module m, FuncWrap w) +{ + FuncWrap p, q; + + 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; +} + +/**/ +#ifdef DYNAMIC + +/**/ +#ifdef AIXDYNAMIC + +#include + +static char *dlerrstr[256]; + +static void * +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); + } + + if (err) { + loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr)); + unload(ret); + ret = NULL; + } + } else + loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr)); + + return ret; +} + +#define dlopen(X,Y) load_and_bind(X) +#define dlclose(X) unload(X) +#define dlerror() (dlerrstr[0]) + +/**/ +#else + +#ifdef HAVE_DLFCN_H +# if defined(HAVE_DL_H) && defined(HPUXDYNAMIC) +# include +# else +# include +# endif +#else +# ifdef HAVE_DL_H +# include +# define RTLD_LAZY BIND_DEFERRED +# define RTLD_GLOBAL DYNAMIC_PATH +# else +# include +# include +# include +# endif +#endif + +/**/ +#ifdef HPUXDYNAMIC +# define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +# define dlclose(handle) shl_unload((shl_t)(handle)) + +static +void * +hpux_dlsym(void *handle, char *name) +{ + void *sym_addr; + if (!shl_findsym((shl_t *)&handle, name, TYPE_UNDEFINED, &sym_addr)) + return sym_addr; + return NULL; +} + +# define dlsym(handle,name) hpux_dlsym(handle,name) +# define dlerror() 0 +#else +# ifndef HAVE_DLCLOSE +# define dlclose(X) ((X), 0) +# endif +/**/ +#endif + +#ifdef DLSYM_NEEDS_UNDERSCORE +# define STR_SETUP "_setup_" +# define STR_BOOT "_boot_" +# define STR_CLEANUP "_cleanup_" +# define STR_FINISH "_finish_" +#else /* !DLSYM_NEEDS_UNDERSCORE */ +# define STR_SETUP "setup_" +# define STR_BOOT "boot_" +# define STR_CLEANUP "cleanup_" +# define STR_FINISH "finish_" +#endif /* !DLSYM_NEEDS_UNDERSCORE */ + +/**/ +#endif /* !AIXDYNAMIC */ + +#ifndef RTLD_LAZY +# define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +# define RTLD_GLOBAL 0 +#endif + +/**/ +static void * +try_load_module(char const *name) +{ + char buf[PATH_MAX + 1]; + char **pp; + void *ret = NULL; + int l; + + l = 1 + strlen(name) + 1 + strlen(DL_EXT); + for (pp = module_path; !ret && *pp; pp++) { + if (l + (**pp ? strlen(*pp) : 1) > PATH_MAX) + continue; + sprintf(buf, "%s/%s.%s", **pp ? *pp : ".", name, DL_EXT); + ret = dlopen(unmeta(buf), RTLD_LAZY | RTLD_GLOBAL); + } + + return ret; +} + +/**/ +static void * +do_load_module(char const *name) +{ + void *ret; + + ret = try_load_module(name); + if (!ret) { + int waserr = errflag; + zerr("failed to load module: %s", name, 0); + errflag = waserr; + } + return ret; +} + +/**/ +#else /* !DYNAMIC */ + +/**/ +static void * +do_load_module(char const *name) +{ + int waserr = errflag; + + zerr("failed to load module: %s", name, 0); + errflag = waserr; + + return NULL; +} + +/**/ +#endif /* !DYNAMIC */ + +/* + * Find a module in the list. + * If aliasp is non-zero, resolve any aliases to the underlying module. + * If namep is set, this is set to point to the last alias value resolved, + * even if that module was not loaded. or the module name if no aliases. + * Hence this is always the physical module to load in a chain of aliases. + * Return NULL if the module named is not stored as a structure, or if we were + * resolving aliases and the final module named is not stored as a + * structure. + * + * TODO: now we have aliases, there may be some merit in using a hash + * table instead of a linked list. + */ +/**/ +static LinkNode +find_module(const char *name, int aliasp, const char **namep) +{ + Module m; + LinkNode node; + + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (!strcmp(m->nam, name)) { + if (aliasp && (m->flags & MOD_ALIAS)) { + if (namep) + *namep = m->u.alias; + return find_module(m->u.alias, 1, namep); + } + if (namep) + *namep = m->nam; + return node; + } + } + return NULL; +} + +/* + * Unlink and free a module node from the linked list. + */ + +/**/ +static void +delete_module(LinkNode node) +{ + Module m = (Module) remnode(modules, node); + + if (m->flags & MOD_ALIAS) + zsfree(m->u.alias); + zsfree(m->nam); + if (m->deps) + freelinklist(m->deps, freestr); + zfree(m, sizeof(*m)); +} + +/**/ +mod_export int +module_loaded(const char *name) +{ + LinkNode node; + Module m; + + return ((node = find_module(name, 1, NULL)) && + (m = ((Module) getdata(node)))->u.handle && + !(m->flags & MOD_UNLOAD)); +} + +/* + * Setup and cleanup functions: we don't search for aliases here, + * since they should have been resolved before we try to load or unload + * the module. + */ + +/**/ +#ifdef DYNAMIC + +/**/ +#ifdef AIXDYNAMIC + +/**/ +static int +dyn_setup_module(Module m) +{ + return ((int (*)_((int,Module))) m->u.handle)(0, m); +} + +/**/ +static int +dyn_boot_module(Module m) +{ + return ((int (*)_((int,Module))) m->u.handle)(1, m); +} + +/**/ +static int +dyn_cleanup_module(Module m) +{ + return ((int (*)_((int,Module))) m->u.handle)(2, m); +} + +/**/ +static int +dyn_finish_module(Module m) +{ + return ((int (*)_((int,Module))) m->u.handle)(3, m); +} + +/**/ +#else + +static Module_func +module_func(Module m, char *name) +{ +#ifdef DYNAMIC_NAME_CLASH_OK + return (Module_func) dlsym(m->u.handle, name); +#else /* !DYNAMIC_NAME_CLASH_OK */ + VARARR(char, buf, strlen(name) + strlen(m->nam)*2 + 1); + char const *p; + char *q; + strcpy(buf, name); + q = strchr(buf, 0); + for(p = m->nam; *p; p++) { + if(*p == '/') { + *q++ = 'Q'; + *q++ = 's'; + } else if(*p == '_') { + *q++ = 'Q'; + *q++ = 'u'; + } else if(*p == 'Q') { + *q++ = 'Q'; + *q++ = 'q'; + } else + *q++ = *p; + } + *q = 0; + return (Module_func) dlsym(m->u.handle, buf); +#endif /* !DYNAMIC_NAME_CLASH_OK */ +} + +/**/ +static int +dyn_setup_module(Module m) +{ + Module_func fn = module_func(m, STR_SETUP); + + if (fn) + return fn(m); + zwarnnam(m->nam, "no setup function", NULL, 0); + return 1; +} + +/**/ +static int +dyn_boot_module(Module m) +{ + Module_func fn = module_func(m, STR_BOOT); + + if(fn) + return fn(m); + zwarnnam(m->nam, "no boot function", NULL, 0); + return 1; +} + +/**/ +static int +dyn_cleanup_module(Module m) +{ + Module_func fn = module_func(m, STR_CLEANUP); + + if(fn) + return fn(m); + zwarnnam(m->nam, "no cleanup function", NULL, 0); + return 1; +} + +/* Note that this function does more than just calling finish_foo(), * + * it really unloads the module. */ + +/**/ +static int +dyn_finish_module(Module m) +{ + Module_func fn = module_func(m, STR_FINISH); + int r; + + if (fn) + r = fn(m); + else { + zwarnnam(m->nam, "no finish function", NULL, 0); + r = 1; + } + dlclose(m->u.handle); + return r; +} + +/**/ +#endif /* !AIXDYNAMIC */ + +/**/ +static int +setup_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->setup)(m) : dyn_setup_module(m)); +} + +/**/ +static int +boot_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->boot)(m) : dyn_boot_module(m)); +} + +/**/ +static int +cleanup_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->cleanup)(m) : dyn_cleanup_module(m)); +} + +/**/ +static int +finish_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? + (m->u.linked->finish)(m) : dyn_finish_module(m)); +} + +/**/ +#else /* !DYNAMIC */ + +/**/ +static int +setup_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? (m->u.linked->setup)(m) : 1); +} + +/**/ +static int +boot_module(Module m) +{ + return ((m->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); +} + +/**/ +static int +finish_module(Module m) +{ + return ((m->flags & MOD_LINKED) ? (m->u.linked->finish)(m) : 1); +} + +/**/ +#endif /* !DYNAMIC */ + +/**/ +static int +modname_ok(char const *p) +{ + do { + if(*p != '_' && !ialnum(*p)) + return 0; + do { + p++; + } while(*p == '_' || ialnum(*p)); + if(!*p) + return 1; + } while(*p++ == '/'); + return 0; +} + +/**/ +mod_export int +load_module(char const *name) +{ + Module m; + void *handle = NULL; + Linkedmod linked; + LinkNode node, n; + int set; + + if (!modname_ok(name)) { + zerr("invalid module name `%s'", name, 0); + return 0; + } + /* + * The following function call may alter name to the final name in a + * chain of aliases. This makes sure the actual module loaded + * is the right one. + */ + queue_signals(); + if (!(node = find_module(name, 1, &name))) { + if (!(linked = module_linked(name)) && + !(handle = do_load_module(name))) { + unqueue_signals(); + return 0; + } + m = zshcalloc(sizeof(*m)); + m->nam = ztrdup(name); + if (handle) { + m->u.handle = handle; + m->flags |= MOD_SETUP; + } else { + m->u.linked = linked; + m->flags |= MOD_SETUP | MOD_LINKED; + } + node = zaddlinknode(modules, m); + + if ((set = setup_module(m)) || boot_module(m)) { + if (!set) + finish_module(m); + delete_module(node); + unqueue_signals(); + return 0; + } + m->flags |= MOD_INIT_S | MOD_INIT_B; + m->flags &= ~MOD_SETUP; + unqueue_signals(); + return 1; + } + m = (Module) getdata(node); + if (m->flags & MOD_SETUP) { + unqueue_signals(); + return 1; + } + if (m->flags & MOD_UNLOAD) + m->flags &= ~MOD_UNLOAD; + else if ((m->flags & MOD_LINKED) ? m->u.linked : m->u.handle) { + unqueue_signals(); + return 1; + } + if (m->flags & MOD_BUSY) { + zerr("circular dependencies for module %s", name, 0); + return 0; + } + m->flags |= MOD_BUSY; + if (m->deps) + for (n = firstnode(m->deps); n; incnode(n)) + if (!load_module((char *) getdata(n))) { + m->flags &= ~MOD_BUSY; + unqueue_signals(); + return 0; + } + m->flags &= ~MOD_BUSY; + if (!m->u.handle) { + handle = NULL; + if (!(linked = module_linked(name)) && + !(handle = do_load_module(name))) { + unqueue_signals(); + return 0; + } + if (handle) { + m->u.handle = handle; + m->flags |= MOD_SETUP; + } else { + m->u.linked = linked; + m->flags |= MOD_SETUP | MOD_LINKED; + } + if (setup_module(m)) { + if (handle) + m->u.handle = NULL; + else + m->u.linked = NULL; + m->flags &= ~MOD_SETUP; + unqueue_signals(); + return 0; + } + m->flags |= MOD_INIT_S; + } + m->flags |= MOD_SETUP; + if (boot_module(m)) { + finish_module(m); + if (m->flags & MOD_LINKED) + m->u.linked = NULL; + else + m->u.handle = NULL; + m->flags &= ~MOD_SETUP; + unqueue_signals(); + return 0; + } + m->flags |= MOD_INIT_B; + m->flags &= ~MOD_SETUP; + unqueue_signals(); + return 1; +} + +/* This ensures that the module with the name given as the second argument + * is loaded. + * The third argument should be non-zero if the function should complain + * about trying to load a module with a full path name in restricted mode. + * The last argument should be non-zero if this function should signal an + * error if the module is already loaded. + * The return value is non-zero if the module was found or loaded. */ + +/**/ +mod_export int +require_module(char *nam, const char *module, UNUSED(int res), int test) +{ + Module m = NULL; + LinkNode node; + int ret = 1; + + /* 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, 0); + return 0; + } + } else + ret = load_module(module); + unqueue_signals(); + + return ret; +} + +/**/ +void +add_dep(const char *name, char *from) +{ + LinkNode node; + Module m; + + /* + * If we were passed an alias, we must resolve it to a final + * module name (and maybe add the corresponding struct), since otherwise + * we would need to check all modules to see if they happen + * to be aliased to the same thing to implement dependencies properly. + * + * This should mean that an attempt to add an alias which would + * have the same name as a module which has dependencies is correctly + * rejected, because then the module named already exists as a non-alias. + * Better make sure. (There's no problem making a an alias which + * *points* to a module with dependencies, of course.) + */ + if (!(node = find_module(name, 1, &name))) { + m = zshcalloc(sizeof(*m)); + m->nam = ztrdup(name); + zaddlinknode(modules, m); + } else + m = (Module) getdata(node); + if (!m->deps) + m->deps = znewlinklist(); + for (node = firstnode(m->deps); + node && strcmp((char *) getdata(node), from); + incnode(node)); + if (!node) + zaddlinknode(m->deps, ztrdup(from)); +} + +/**/ +static void +autoloadscan(HashNode hn, int printflags) +{ + Builtin bn = (Builtin) hn; + + if(bn->flags & BINF_ADDED) + return; + if(printflags & PRINT_LIST) { + fputs("zmodload -ab ", stdout); + if(bn->optstr[0] == '-') + fputs("-- ", stdout); + quotedzputs(bn->optstr, stdout); + if(strcmp(bn->nam, bn->optstr)) { + putchar(' '); + quotedzputs(bn->nam, stdout); + } + } else { + nicezputs(bn->nam, stdout); + if(strcmp(bn->nam, bn->optstr)) { + fputs(" (", stdout); + nicezputs(bn->optstr, stdout); + putchar(')'); + } + } + putchar('\n'); +} + +#ifndef __SYMBIAN32__ +/**/ +int +bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) +{ + int ops_bcpf = OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c') || + OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f'); + int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u'); + int ret = 1; + + if (ops_bcpf && !ops_au) { + zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u", + NULL, 0); + return 1; + } + if (OPT_ISSET(ops,'A') || OPT_ISSET(ops,'R')) { + if (ops_bcpf || ops_au || OPT_ISSET(ops,'d') || + (OPT_ISSET(ops,'R') && OPT_ISSET(ops,'e'))) { + zwarnnam(nam, "illegal flags combined with -A or -R", NULL, 0); + return 1; + } + if (!OPT_ISSET(ops,'e')) + return bin_zmodload_alias(nam, args, ops); + } + if (OPT_ISSET(ops,'d') && OPT_ISSET(ops,'a')) { + zwarnnam(nam, "-d cannot be combined with -a", NULL, 0); + return 1; + } + if (OPT_ISSET(ops,'u') && !*args) { + zwarnnam(nam, "what do you want to unload?", NULL, 0); + return 1; + } + if (OPT_ISSET(ops,'e') && (OPT_ISSET(ops,'I') || OPT_ISSET(ops,'L') || + OPT_ISSET(ops,'a') || OPT_ISSET(ops,'d') || + OPT_ISSET(ops,'i') || OPT_ISSET(ops,'u'))) { + zwarnnam(nam, "-e cannot be combined with other options", NULL, 0); + return 1; + } + queue_signals(); + if (OPT_ISSET(ops,'e')) + ret = bin_zmodload_exist(nam, args, ops); + else if (OPT_ISSET(ops,'d')) + ret = bin_zmodload_dep(nam, args, ops); + else if ((OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b')) && + !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f'))) + ret = bin_zmodload_auto(nam, args, ops); + else if (OPT_ISSET(ops,'c') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p'))) + ret = bin_zmodload_cond(nam, args, ops); + else if (OPT_ISSET(ops,'f') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p'))) + ret = bin_zmodload_math(nam, args, ops); + else if (OPT_ISSET(ops,'p') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c'))) + ret = bin_zmodload_param(nam, args, ops); + else if (!(OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b') || + OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p'))) + ret = bin_zmodload_load(nam, args, ops); + else + zwarnnam(nam, "use only one of -b, -c, or -p", NULL, 0); + unqueue_signals(); + + return ret; +} +#endif //#ifndef __SYMBIAN32__ + +/**/ +static int +bin_zmodload_alias(char *nam, char **args, Options ops) +{ + /* + * TODO: while it would be too nasty to have aliases, as opposed + * to real loadable modules, with dependencies --- just what would + * we need to load when, exactly? --- there is in principle no objection + * to making it possible to force an alias onto an existing unloaded + * module which has dependencies. This would simply transfer + * the dependencies down the line to the aliased-to module name. + * This is actually useful, since then you can alias zsh/zle=mytestzle + * to load another version of zle. But then what happens when the + * alias is removed? Do you transfer the dependencies back? And + * suppose other names are aliased to the same file? It might be + * kettle of fish best left unwormed. + */ + LinkNode node; + Module m; + int ret = 0; + + if (!*args) { + if (OPT_ISSET(ops,'R')) { + zwarnnam(nam, "no module alias to remove", NULL, 0); + return 1; + } + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (m->flags & MOD_ALIAS) + printmodalias(m, ops); + } + return 0; + } + + for (; !ret && *args; args++) { + char *eqpos = strchr(*args, '='); + char *aliasname = eqpos ? eqpos+1 : NULL; + if (eqpos) + *eqpos = '\0'; + if (!modname_ok(*args)) { + zwarnnam(nam, "invalid module name `%s'", *args, 0); + return 1; + } + if (OPT_ISSET(ops,'R')) { + if (aliasname) { + zwarnnam(nam, "bad syntax for removing module alias: %s", + *args, 0); + return 1; + } + node = find_module(*args, 0, NULL); + if (node) { + m = (Module) getdata(node); + if (!(m->flags & MOD_ALIAS)) { + zwarnnam(nam, "module is not an alias: %s", *args, 0); + ret = 1; + break; + } + delete_module(node); + } else { + zwarnnam(nam, "no such module alias: %s", *args, 0); + return 1; + } + } else { + if (aliasname) { + const char *mname = aliasname; + if (!modname_ok(aliasname)) { + zwarnnam(nam, "invalid module name `%s'", aliasname, 0); + return 1; + } + find_module(aliasname, 1, &mname); + if (!strcmp(mname, *args)) { + zwarnnam(nam, "module alias would refer to itself: %s", + *args, 0); + return 1; + } + node = find_module(*args, 0, NULL); + if (node) { + m = (Module) getdata(node); + if (!(m->flags & MOD_ALIAS)) { + zwarnnam(nam, "module is not an alias: %s", *args, 0); + return 1; + } + zsfree(m->u.alias); + } else { + m = (Module) zshcalloc(sizeof(*m)); + m->nam = ztrdup(*args); + m->flags = MOD_ALIAS; + zaddlinknode(modules, m); + } + m->u.alias = ztrdup(aliasname); + } else { + if ((node = find_module(*args, 0, NULL))) { + m = (Module) getdata(node); + if (m->flags & MOD_ALIAS) + printmodalias(m, ops); + else { + zwarnnam(nam, "module is not an alias: %s", + *args, 0); + return 1; + } + } else { + zwarnnam(nam, "no such module alias: %s", *args, 0); + return 1; + } + } + } + } + + return 0; +} + +/**/ +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, 1, NULL))) + m = (Module) getdata(node2); + else + continue; + } + if (m->u.handle && !(m->flags & MOD_UNLOAD)) { + nicezputs(modname, stdout); + putchar('\n'); + } + } + return 0; + } else { + int ret = 0; + + for (; !ret && *args; args++) { + if (!(node = find_module(*args, 1, NULL)) + || !(m = (Module) getdata(node))->u.handle + || (m->flags & MOD_UNLOAD)) + ret = 1; + } + return ret; + } +} + +/**/ +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, 1, &tnam); + if (!node) + return 0; + m = (Module) getdata(node); + if (*args && m->deps) { + do { + LinkNode dnode; + for (dnode = firstnode(m->deps); dnode; incnode(dnode)) + if (!strcmp(*args, getdata(dnode))) { + zsfree(getdata(dnode)); + remnode(m->deps, dnode); + break; + } + } while(*++args); + if (empty(m->deps)) { + freelinklist(m->deps, freestr); + m->deps = NULL; + } + } else { + if (m->deps) { + freelinklist(m->deps, freestr); + m->deps = NULL; + } + } + if (!m->deps && !m->u.handle) + delete_module(node); + return 0; + } else if (!args[0] || !args[1]) { + /* list dependencies */ + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (m->deps && (!args[0] || !strcmp(args[0], m->nam))) { + LinkNode n; + if (OPT_ISSET(ops,'L')) { + printf("zmodload -d "); + if(m->nam[0] == '-') + fputs("-- ", stdout); + quotedzputs(m->nam, stdout); + } else { + nicezputs(m->nam, stdout); + putchar(':'); + } + for (n = firstnode(m->deps); n; incnode(n)) { + putchar(' '); + if(OPT_ISSET(ops,'L')) + quotedzputs((char *) getdata(n), stdout); + else + nicezputs((char *) getdata(n), stdout); + } + putchar('\n'); + } + } + return 0; + } else { + /* add dependencies */ + int ret = 0; + char *tnam = *args++; + + for (; *args; args++) + add_dep(tnam, *args); + return ret; + } +} + +/**/ +static int +bin_zmodload_auto(char *nam, char **args, Options ops) +{ + int ret = 0; + if(OPT_ISSET(ops,'u')) { + /* remove autoloaded builtins */ + for (; *args; args++) { + Builtin bn = (Builtin) builtintab->getnode2(builtintab, *args); + if (!bn) { + if(!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "%s: no such builtin", *args, 0); + ret = 1; + } + } else if (bn->flags & BINF_ADDED) { + zwarnnam(nam, "%s: builtin is already defined", *args, 0); + ret = 1; + } else + deletebuiltin(*args); + } + return ret; + } else if(!*args) { + /* list autoloaded builtins */ + scanhashtable(builtintab, 0, 0, 0, + autoloadscan, OPT_ISSET(ops,'L') ? PRINT_LIST : 0); + return 0; + } else { + /* add autoloaded builtins */ + char *modnam; + modnam = *args++; + do { + char *bnam = *args ? *args++ : modnam; + if (strchr(bnam, '/')) { + zwarnnam(nam, "%s: `/' is illegal in a builtin", bnam, 0); + ret = 1; + } else if (add_autobin(bnam, modnam) && !OPT_ISSET(ops,'i')) { + zwarnnam(nam, "failed to add builtin %s", bnam, 0); + ret = 1; + } + } while(*args); + return ret; + } +} + +/**/ +static int +bin_zmodload_cond(char *nam, char **args, Options ops) +{ + int ret = 0; + + if (OPT_ISSET(ops,'u')) { + /* remove autoloaded conditions */ + for (; *args; args++) { + Conddef cd = getconddef(OPT_ISSET(ops,'I'), *args, 0); + + if (!cd) { + if (!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "%s: no such condition", *args, 0); + ret = 1; + } + } else if (cd->flags & CONDF_ADDED) { + zwarnnam(nam, "%s: condition is already defined", *args, 0); + ret = 1; + } else + deleteconddef(cd); + } + return ret; + } else if (!*args) { + /* list autoloaded conditions */ + Conddef p; + + for (p = condtab; p; p = p->next) { + if (p->module) { + if (OPT_ISSET(ops,'L')) { + fputs("zmodload -ac", stdout); + if (p->flags & CONDF_INFIX) + putchar('I'); + printf(" %s %s\n", p->module, p->name); + } else { + if (p->flags & CONDF_INFIX) + fputs("infix ", stdout); + else + fputs("post ", stdout); + printf("%s (%s)\n",p->name, p->module); + } + } + } + return 0; + } else { + /* add autoloaded conditions */ + char *modnam; + + modnam = *args++; + do { + char *cnam = *args ? *args++ : modnam; + if (strchr(cnam, '/')) { + zwarnnam(nam, "%s: `/' is illegal in a condition", cnam, 0); + ret = 1; + } else if (add_autocond(cnam, OPT_ISSET(ops,'I'), modnam) && + !OPT_ISSET(ops,'i')) { + zwarnnam(nam, "failed to add condition `%s'", cnam, 0); + ret = 1; + } + } while(*args); + return ret; + } +} + +/**/ +static int +bin_zmodload_math(char *nam, char **args, Options ops) +{ + int ret = 0; + + if (OPT_ISSET(ops,'u')) { + /* remove autoloaded math functions */ + for (; *args; args++) { + MathFunc f = getmathfunc(*args, 0); + + if (!f) { + if (!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "%s: no such math function", *args, 0); + ret = 1; + } + } else if (f->flags & MFF_ADDED) { + zwarnnam(nam, "%s: math function is already defined", *args, 0); + ret = 1; + } else + deletemathfunc(f); + } + return ret; + } else if (!*args) { + /* list autoloaded math functions */ + MathFunc p; + + for (p = mathfuncs; p; p = p->next) { + if (p->module) { + if (OPT_ISSET(ops,'L')) { + fputs("zmodload -af", stdout); + printf(" %s %s\n", p->module, p->name); + } else + printf("%s (%s)\n",p->name, p->module); + } + } + return 0; + } else { + /* add autoloaded math functions */ + char *modnam; + + modnam = *args++; + do { + char *fnam = *args ? *args++ : modnam; + if (strchr(fnam, '/')) { + zwarnnam(nam, "%s: `/' is illegal in a math function", + fnam, 0); + ret = 1; + } else if (add_automathfunc(fnam, modnam) && !OPT_ISSET(ops,'i')) { + zwarnnam(nam, "failed to add math function `%s'", fnam, 0); + ret = 1; + } + } while(*args); + return ret; + } +} + +static void +printautoparams(HashNode hn, int lon) +{ + Param pm = (Param) hn; + + if (pm->flags & PM_AUTOLOAD) { + if (lon) + printf("zmodload -ap %s %s\n", pm->u.str, pm->nam); + else + printf("%s (%s)\n", pm->nam, pm->u.str); + } +} + +/**/ +static int +bin_zmodload_param(char *nam, char **args, Options ops) +{ + int ret = 0; + + if (OPT_ISSET(ops,'u')) { + /* remove autoloaded parameters */ + for (; *args; args++) { + Param pm = (Param) gethashnode2(paramtab, *args); + + if (!pm) { + if (!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "%s: no such parameter", *args, 0); + ret = 1; + } + } else if (!(pm->flags & PM_AUTOLOAD)) { + zwarnnam(nam, "%s: parameter is already defined", *args, 0); + ret = 1; + } else + unsetparam_pm(pm, 0, 1); + } + return ret; + } else if (!*args) { + scanhashtable(paramtab, 1, 0, 0, printautoparams, OPT_ISSET(ops,'L')); + return 0; + } else { + /* add autoloaded parameters */ + char *modnam; + + modnam = *args++; + do { + char *pnam = *args ? *args++ : modnam; + if (strchr(pnam, '/')) { + zwarnnam(nam, "%s: `/' is illegal in a parameter", pnam, 0); + ret = 1; + } else + add_autoparam(pnam, modnam); + } while(*args); + return ret; + } +} + +/**/ +int +unload_module(Module m, LinkNode node) +{ + /* + * Only unload the real module, so resolve aliases. + */ + if (m->flags & MOD_ALIAS) { + LinkNode node = find_module(m->u.alias, 1, NULL); + if (!node) + return 1; + m = (Module) getdata(node); + } + if ((m->flags & MOD_INIT_S) && + !(m->flags & MOD_UNLOAD) && + ((m->flags & MOD_LINKED) ? + (m->u.linked && m->u.linked->cleanup(m)) : + (m->u.handle && cleanup_module(m)))) + return 1; + else { + int del = (m->flags & MOD_UNLOAD); + + if (m->wrapper) { + m->flags |= MOD_UNLOAD; + return 0; + } + m->flags &= ~MOD_UNLOAD; + if (m->flags & MOD_INIT_B) { + if (m->flags & MOD_LINKED) { + if (m->u.linked) { + m->u.linked->finish(m); + m->u.linked = NULL; + } + } else { + if (m->u.handle) { + finish_module(m); + m->u.handle = NULL; + } + } + } + if (del && m->deps) { + /* The module was unloaded delayed, unload all modules * + * on which it depended. */ + LinkNode n; + + for (n = firstnode(m->deps); n; incnode(n)) { + LinkNode dn = find_module((char *) getdata(n), 1, 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 (du) + unload_module(dm, NULL); + } + } + } + if(!m->deps) { + if (!node) { + for (node = firstnode(modules); node; incnode(node)) + if (m == (Module) getdata(node)) + break; + if (!node) + return 1; + } + delete_module(node); + } + } + return 0; +} + +/**/ +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 */ + const char *mname = *args; + for(; *args; args++) { + node = find_module(*args, 1, &mname); + if (node) { + LinkNode mn, dn; + int del = 0; + + for (mn = firstnode(modules); mn; incnode(mn)) { + m = (Module) getdata(mn); + if (m->deps && m->u.handle) + for (dn = firstnode(m->deps); dn; incnode(dn)) + if (!strcmp((char *) getdata(dn), mname)) { + if (m->flags & MOD_UNLOAD) + del = 1; + else { + zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", mname, 0); + ret = 1; + goto cont; + } + } + } + m = (Module) getdata(node); + if (del) + m->wrapper++; + if (unload_module(m, node)) + ret = 1; + if (del) + m->wrapper--; + } else if (!OPT_ISSET(ops,'i')) { + zwarnnam(nam, "no such module %s", *args, 0); + ret = 1; + } + cont: ; + } + return ret; + } else if(!*args) { + /* list modules */ + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (m->u.handle && !(m->flags & (MOD_UNLOAD|MOD_ALIAS))) { + if(OPT_ISSET(ops,'L')) { + printf("zmodload "); + if(m->nam[0] == '-') + fputs("-- ", stdout); + quotedzputs(m->nam, stdout); + } else + nicezputs(m->nam, stdout); + putchar('\n'); + } + } + return 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(p->module); + 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, 0); + hadf = 1; + } else { + c->flags |= CONDF_ADDED; + hads = 2; + } + 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, 0); + hadf = 1; + } else + hads = 2; + h++; + } + return hadf ? hads : 1; +} + +/* Delete hook definitions. */ + +/**/ +int +deletehookdef(Hookdef h) +{ + Hookdef p, q; + + for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next); + + if (!p) + return 1; + + if (q) + q->next = p->next; + else + hooktab = p->next; + freelinklist(p->funcs, NULL); + return 0; +} + +/**/ +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; + + pm->level = 0; + pm->u.data = d->var; + if (d->gsu) + pm->gsu.i = (GsuInteger) d->gsu; + else { + /* + * If no get/set/unset class, use the appropriate + * variable type. + */ + switch (PM_TYPE(pm->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); + 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, 0); + 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)); + } + 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 (deleteconddef(c)) { + zwarnnam(nam, "condition `%s' already deleted", c->name, 0); + hadf = 1; + } else + hads = 2; + c->flags &= ~CONDF_ADDED; + c++; + } + 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->flags |= PM_AUTOLOAD; + unqueue_signals(); +} + +/* List of math functions. */ + +/**/ +MathFunc mathfuncs; + +/**/ +static 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) { + char *n = dupstring(p->module); + + removemathfunc(q, p); + + load_module(n); + + return getmathfunc(name, 0); + } + return p; + } + + return NULL; +} + +/**/ +mod_export 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) { + /* + * Autoloadable, replace. + */ + removemathfunc(q, p); + break; + } + return 1; + } + + f->flags |= MFF_ADDED; + f->next = mathfuncs; + mathfuncs = f; + + return 0; +} + +/**/ +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, 0); + 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; +} + +/**/ +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; + + 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 +deletemathfuncs(char const *nam, MathFunc f, int size) +{ + 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, 0); + hadf = 1; + } else + hads = 2; + f++; + } + return hadf ? hads : 1; +}