1
0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-10-03 09:21:19 +02:00
zsh/Src/module.c
1999-04-15 18:05:38 +00:00

652 lines
15 KiB
C

/*
* module.c - deal with dynamic modules
*
* 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"
/* The `zsh' 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
boot_zsh(Module m)
{
return 0;
}
/* 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));
PERMALLOC {
builtintab->addnode(builtintab, b->nam, b);
} LASTALLOC;
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)); */
/**/
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;
}
#ifdef DYNAMIC
/* $module_path ($MODULE_PATH) */
/**/
char **module_path;
/* List of modules */
/**/
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 = zcalloc(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. */
/**/
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;
}
#ifdef HAVE_DLFCN_H
# include <dlfcn.h>
#else
# include <sys/types.h>
# include <nlist.h>
# include <link.h>
#endif
#ifndef RTLD_LAZY
# define RTLD_LAZY 1
#endif
#ifndef RTLD_GLOBAL
# define RTLD_GLOBAL 0
#endif
#ifndef HAVE_DLCLOSE
# define dlclose(X) ((X), 0)
#endif
#ifdef DLSYM_NEEDS_UNDERSCORE
# define STR_BOOT "_boot_"
# define STR_BOOT_S "_boot_%s"
# define STR_CLEANUP "_cleanup_"
# define STR_CLEANUP_S "_cleanup_%s"
#else /* !DLSYM_NEEDS_UNDERSCORE */
# define STR_BOOT "boot_"
# define STR_BOOT_S "boot_%s"
# define STR_CLEANUP "cleanup_"
# define STR_CLEANUP_S "cleanup_%s"
#endif /* !DLSYM_NEEDS_UNDERSCORE */
typedef int (*Module_func) _((Module));
/**/
static void *
try_load_module(char const *name)
{
char buf[PATH_MAX + 1];
char **pp;
void *ret = NULL;
int l;
if (strchr(name, '/')) {
ret = dlopen(unmeta(name), RTLD_LAZY | RTLD_GLOBAL);
if (ret ||
unset(PATHDIRS) ||
(*name == '/') ||
(*name == '.' && name[1] == '/') ||
(*name == '.' && name[1] == '.' && name[2] == '/'))
return ret;
}
l = strlen(name) + 1;
for (pp = module_path; !ret && *pp; pp++) {
if (l + (**pp ? strlen(*pp) : 1) > PATH_MAX)
continue;
sprintf(buf, "%s/%s", **pp ? *pp : ".", name);
ret = dlopen(unmeta(buf), RTLD_LAZY | RTLD_GLOBAL);
}
return ret;
}
/**/
static void *
do_load_module(char const *name)
{
void *ret = NULL;
char buf[PATH_MAX + 1];
if (strlen(name) + strlen(DL_EXT) < PATH_MAX) {
sprintf(buf, "%s.%s", name, DL_EXT);
ret = try_load_module(buf);
}
if (!ret)
ret = try_load_module(name);
if (!ret) {
int waserr = errflag;
zerr("failed to load module: %s", name, 0);
errflag = waserr;
}
return ret;
}
/**/
static LinkNode
find_module(const char *name)
{
Module m;
LinkNode node;
for (node = firstnode(modules); node; incnode(node)) {
m = (Module) getdata(node);
if (!strcmp(m->nam, name))
return node;
}
return NULL;
}
/**/
static int
init_module(Module m)
{
char *s, *t;
#ifndef DYNAMIC_NAME_CLASH_OK
char buf[PATH_MAX + 1];
#endif
Module_func fn;
s = strrchr(m->nam, '/');
if (s)
s = dupstring(++s);
else
s = m->nam;
if ((t = strrchr(s, '.')))
*t = '\0';
#ifdef DYNAMIC_NAME_CLASH_OK
fn = (Module_func) dlsym(m->handle, STR_BOOT);
#else /* !DYNAMIC_NAME_CLASH_OK */
if (strlen(s) + 6 > PATH_MAX)
return 1;
sprintf(buf, STR_BOOT_S, s);
fn = (Module_func) dlsym(m->handle, buf);
#endif /* !DYNAMIC_NAME_CLASH_OK */
if(fn)
return fn(m);
zwarnnam(m->nam, "no boot function", NULL, 0);
return 1;
}
/**/
Module
load_module(char const *name)
{
Module m;
void *handle;
LinkNode node, n;
if (!(node = find_module(name))) {
if (!(handle = do_load_module(name)))
return NULL;
m = zcalloc(sizeof(*m));
m->nam = ztrdup(name);
m->handle = handle;
if (init_module(m)) {
dlclose(handle);
zsfree(m->nam);
zfree(m, sizeof(*m));
return NULL;
}
PERMALLOC {
addlinknode(modules, m);
} LASTALLOC;
return m;
}
m = (Module) getdata(node);
if (m->handle)
return m;
if (m->flags & MOD_BUSY) {
zerr("circular dependencies for module %s", name, 0);
return NULL;
}
m->flags |= MOD_BUSY;
for (n = firstnode(m->deps); n; incnode(n))
if (!load_module((char *) getdata(n))) {
m->flags &= ~MOD_BUSY;
return NULL;
}
m->flags &= ~MOD_BUSY;
if (!(m->handle = do_load_module(name)))
return NULL;
if (init_module(m)) {
dlclose(m->handle);
m->handle = NULL;
return NULL;
}
return m;
}
/**/
static int
cleanup_module(Module m)
{
char *s, *t;
#ifndef DYNAMIC_NAME_CLASH_OK
char buf[PATH_MAX + 1];
#endif
Module_func fn;
s = strrchr(m->nam, '/');
if (s)
s = dupstring(++s);
else
s = m->nam;
if ((t = strrchr(s, '.')))
*t = '\0';
#ifdef DYNAMIC_NAME_CLASH_OK
fn = (Module_func) dlsym(m->handle, STR_CLEANUP);
#else /* !DYNAMIC_NAME_CLASH_OK */
if (strlen(s) + 9 > PATH_MAX)
return 1;
sprintf(buf, STR_CLEANUP_S, s);
fn = (Module_func) dlsym(m->handle, buf);
#endif /* !DYNAMIC_NAME_CLASH_OK */
if(fn)
return fn(m);
zwarnnam(m->nam, "no cleanup function", NULL, 0);
return 1;
}
/**/
void
add_dep(char *name, char *from)
{
LinkNode node;
Module m;
PERMALLOC {
if (!(node = find_module(name))) {
m = zcalloc(sizeof(*m));
m->nam = ztrdup(name);
addlinknode(modules, m);
} else
m = (Module) getdata(node);
if (!m->deps)
m->deps = newlinklist();
for (node = firstnode(m->deps);
node && strcmp((char *) getdata(node), from);
incnode(node));
if (!node)
addlinknode(m->deps, ztrdup(from));
} LASTALLOC;
}
/**/
static void
autoloadscan(HashNode hn, int printflags)
{
Builtin bn = (Builtin) hn;
if(bn->flags & BINF_ADDED)
return;
if(printflags & PRINT_LIST) {
fputs("zmodload -a ", 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');
}
/**/
int
bin_zmodload(char *nam, char **args, char *ops, int func)
{
if(ops['d'] && ops['a']) {
zwarnnam(nam, "-d cannot be combined with -a", NULL, 0);
return 1;
}
if (ops['u'] && !*args) {
zwarnnam(nam, "what do you want to unload?", NULL, 0);
return 1;
}
if(ops['d'])
return bin_zmodload_dep(nam, args, ops);
else if(ops['a'])
return bin_zmodload_auto(nam, args, ops);
else
return bin_zmodload_load(nam, args, ops);
}
/**/
static int
bin_zmodload_dep(char *nam, char **args, char *ops)
{
LinkNode node;
Module m;
if(ops['u']) {
/* remove dependencies */
char *tnam = *args++;
node = find_module(tnam);
if (!node)
return 0;
m = (Module) getdata(node);
if(*args && m->deps) {
do {
for(node = firstnode(m->deps); node; incnode(node))
if(!strcmp(*args, getdata(node))) {
zsfree(getdata(node));
remnode(m->deps, node);
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->handle) {
remnode(modules, node);
zsfree(m->nam);
zfree(m, sizeof(*m));
}
return 0;
} else if(!args[0] || !args[1]) {
/* list dependencies */
for (node = firstnode(modules); node; incnode(node)) {
m = (Module) getdata(node);
if (m->deps && (!args[0] || !strcmp(args[0], m->nam))) {
LinkNode n;
if(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(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++) {
if(isset(RESTRICTED) && strchr(*args, '/')) {
zwarnnam(nam, "%s: restricted", *args, 0);
ret = 1;
} else
add_dep(tnam, *args);
}
return ret;
}
}
/**/
static int
bin_zmodload_auto(char *nam, char **args, char *ops)
{
int ret = 0;
if(ops['u']) {
/* remove autoloaded builtins */
for (; *args; args++) {
Builtin bn = (Builtin) builtintab->getnode2(builtintab, *args);
if (!bn) {
if(!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, ops['L'] ? PRINT_LIST : 0);
return 0;
} else {
/* add autoloaded builtins */
char *modnam;
modnam = *args++;
if(isset(RESTRICTED) && strchr(modnam, '/')) {
zwarnnam(nam, "%s: restricted", modnam, 0);
return 1;
}
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) && !ops['i']) {
zwarnnam(nam, "failed to add builtin %s", bnam, 0);
ret = 1;
}
} while(*args);
return ret;
}
}
/**/
static int
bin_zmodload_load(char *nam, char **args, char *ops)
{
LinkNode node;
Module m;
int ret = 0;
if(ops['u']) {
/* unload modules */
for(; *args; args++) {
node = find_module(*args);
if (node) {
LinkNode mn, dn;
for (mn = firstnode(modules); mn; incnode(mn)) {
m = (Module) getdata(mn);
if (m->deps && m->handle)
for (dn = firstnode(m->deps); dn; incnode(dn))
if (!strcmp((char *) getdata(dn), *args)) {
zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", *args, 0);
ret = 1;
goto cont;
}
}
m = (Module) getdata(node);
if (m->handle && cleanup_module(m))
ret = 1;
else {
if (m->handle)
dlclose(m->handle);
m->handle = NULL;
if(!m->deps) {
remnode(modules, node);
zsfree(m->nam);
zfree(m, sizeof(*m));
}
}
} else if (!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->handle) {
if(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++) {
node = find_module(*args);
if (node && ((Module) getdata(node))->handle) {
if (!ops['i']) {
zwarnnam(nam, "module %s already loaded.", *args, 0);
ret = 1;
}
} else if (isset(RESTRICTED) && strchr(*args, '/')) {
zwarnnam(nam, "%s: restricted", *args, 0);
ret = 1;
} else if (!load_module(*args))
ret = 1;
}
return ret;
}
}
#endif /* DYNAMIC */