1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-01-01 05:16:05 +01:00
zsh/Src/Modules/db_gdbm.c
2016-01-30 00:26:23 +09:00

403 lines
8.5 KiB
C

/*
* db_gdbm.c - bindings for gdbm
*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 2008 Clint Adams
* 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 Clint Adams 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 Peter Stephenson, Sven Wischnowsky and the Zsh
* Development Group have been advised of the possibility of such damage.
*
* Clint Adams 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 Peter
* Stephenson, Sven Wischnowsky and the Zsh Development Group have no
* obligation to provide maintenance, support, updates, enhancements, or
* modifications.
*
*/
#include "db_gdbm.mdh"
#include "db_gdbm.pro"
/*
* Make sure we have all the bits I'm using for memory mapping, otherwise
* I don't know what I'm doing.
*/
#if defined(HAVE_GDBM_H) && defined(HAVE_GDBM_OPEN)
#include <gdbm.h>
static char *backtype = "db/gdbm";
static const struct gsu_scalar gdbm_gsu =
{ gdbmgetfn, gdbmsetfn, gdbmunsetfn };
/**/
static const struct gsu_hash gdbm_hash_gsu =
{ hashgetfn, gdbmhashsetfn, gdbmhashunsetfn };
static struct builtin bintab[] = {
BUILTIN("ztie", 0, bin_ztie, 1, -1, 0, "d:f:r", NULL),
BUILTIN("zuntie", 0, bin_zuntie, 1, -1, 0, "u", NULL),
};
/**/
static int
bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
{
char *resource_name, *pmname;
GDBM_FILE dbf = NULL;
int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE;
Param tied_param;
if(!OPT_ISSET(ops,'d')) {
zwarnnam(nam, "you must pass `-d %s'", backtype);
return 1;
}
if(!OPT_ISSET(ops,'f')) {
zwarnnam(nam, "you must pass `-f' with a filename", NULL);
return 1;
}
if (OPT_ISSET(ops,'r')) {
read_write |= GDBM_READER;
pmflags |= PM_READONLY;
} else {
read_write |= GDBM_WRCREAT;
}
/* Here should be a lookup of the backend type against
* a registry.
*/
if (strcmp(OPT_ARG(ops, 'd'), backtype) != 0) {
zwarnnam(nam, "unsupported backend type `%s'", OPT_ARG(ops, 'd'));
return 1;
}
resource_name = OPT_ARG(ops, 'f');
pmname = *args;
if ((tied_param = (Param)paramtab->getnode(paramtab, pmname)) &&
!(tied_param->node.flags & PM_UNSET)) {
/*
* Unset any existing parameter. Note there's no implicit
* "local" here, but if the existing parameter is local
* that will be reflected in the new one.
*
* We need to do this before attempting to open the DB
* in case this variable is already tied to a DB.
*
* This can fail if the variable is readonly or restricted.
* We could call unsetparam() and check errflag instead
* of the return status.
*/
if (unsetparam_pm(tied_param, 0, 1))
return 1;
}
dbf = gdbm_open(resource_name, 0, read_write, 0666, 0);
if(dbf)
addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL);
else {
zwarnnam(nam, "error opening database file %s", resource_name);
return 1;
}
if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys,
pmflags))) {
zwarnnam(nam, "cannot create the requested parameter %s", pmname);
fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED;
gdbm_close(dbf);
return 1;
}
tied_param->gsu.h = &gdbm_hash_gsu;
tied_param->u.hash->tmpdata = (void *)dbf;
return 0;
}
/**/
static int
bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
{
Param pm;
char *pmname;
int ret = 0;
for (pmname = *args; *args++; pmname = *args) {
pm = (Param) paramtab->getnode(paramtab, pmname);
if(!pm) {
zwarnnam(nam, "cannot untie %s", pmname);
ret = 1;
continue;
}
if (pm->gsu.h != &gdbm_hash_gsu) {
zwarnnam(nam, "not a tied gdbm hash: %s", pmname);
ret = 1;
continue;
}
queue_signals();
if (OPT_ISSET(ops,'u'))
gdbmuntie(pm); /* clear read-only-ness */
if (unsetparam_pm(pm, 0, 1)) {
/* assume already reported */
ret = 1;
}
unqueue_signals();
}
return ret;
}
/**/
static char *
gdbmgetfn(Param pm)
{
datum key, content;
int ret;
GDBM_FILE dbf;
key.dptr = pm->node.nam;
key.dsize = strlen(key.dptr) + 1;
dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
ret = gdbm_exists(dbf, key);
if(ret) {
content = gdbm_fetch(dbf, key);
} else {
content.dptr = dupstring("");
}
return content.dptr;
}
/**/
static void
gdbmsetfn(Param pm, char *val)
{
datum key, content;
GDBM_FILE dbf;
key.dptr = pm->node.nam;
key.dsize = strlen(key.dptr) + 1;
content.dptr = val;
content.dsize = strlen(content.dptr) + 1;
dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
(void)gdbm_store(dbf, key, content, GDBM_REPLACE);
}
/**/
static void
gdbmunsetfn(Param pm, UNUSED(int um))
{
datum key;
GDBM_FILE dbf;
key.dptr = pm->node.nam;
key.dsize = strlen(key.dptr) + 1;
dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
(void)gdbm_delete(dbf, key);
}
/**/
static HashNode
getgdbmnode(HashTable ht, const char *name)
{
int len;
char *nameu;
Param pm = NULL;
nameu = dupstring(name);
unmetafy(nameu, &len);
pm = (Param) hcalloc(sizeof(struct param));
pm->node.nam = nameu;
pm->node.flags = PM_SCALAR;
pm->gsu.s = &gdbm_gsu;
pm->u.hash = ht;
return &pm->node;
}
/**/
static void
scangdbmkeys(HashTable ht, ScanFunc func, int flags)
{
Param pm = NULL;
datum key, content;
GDBM_FILE dbf = (GDBM_FILE)(ht->tmpdata);
pm = (Param) hcalloc(sizeof(struct param));
pm->node.flags = PM_SCALAR;
pm->gsu.s = &nullsetscalar_gsu;
key = gdbm_firstkey(dbf);
while(key.dptr) {
content = gdbm_fetch(dbf, key);
pm->node.nam = key.dptr;
pm->u.str = content.dptr;
pm->gsu.s = &nullsetscalar_gsu;
func(&pm->node, flags);
key = gdbm_nextkey(dbf, key);
}
}
/**/
static void
gdbmhashsetfn(Param pm, HashTable ht)
{
int i;
HashNode hn;
GDBM_FILE dbf;
datum key, content;
if (!pm->u.hash || pm->u.hash == ht)
return;
if (!(dbf = (GDBM_FILE)(pm->u.hash->tmpdata)))
return;
key = gdbm_firstkey(dbf);
while (key.dptr) {
queue_signals();
(void)gdbm_delete(dbf, key);
free(key.dptr);
unqueue_signals();
key = gdbm_firstkey(dbf);
}
/* just deleted everything, clean up */
(void)gdbm_reorganize(dbf);
if (!ht)
return;
for (i = 0; i < ht->hsize; i++)
for (hn = ht->nodes[i]; hn; hn = hn->next) {
struct value v;
v.isarr = v.flags = v.start = 0;
v.end = -1;
v.arr = NULL;
v.pm = (Param) hn;
key.dptr = v.pm->node.nam;
key.dsize = strlen(key.dptr) + 1;
queue_signals();
content.dptr = getstrvalue(&v);
content.dsize = strlen(content.dptr) + 1;
(void)gdbm_store(dbf, key, content, GDBM_REPLACE);
unqueue_signals();
}
}
/**/
static void
gdbmuntie(Param pm)
{
GDBM_FILE dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
HashTable ht = pm->u.hash;
if (dbf) { /* paranoia */
fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED;
gdbm_close(dbf);
}
ht->tmpdata = NULL;
/* for completeness ... createspecialhash() should have an inverse */
ht->getnode = ht->getnode2 = gethashnode2;
ht->scantab = NULL;
pm->node.flags &= ~(PM_SPECIAL|PM_READONLY);
pm->gsu.h = &stdhash_gsu;
}
/**/
static void
gdbmhashunsetfn(Param pm, UNUSED(int exp))
{
gdbmuntie(pm);
/* hash table is now normal, so proceed normally... */
pm->gsu.h->setfn(pm, NULL);
pm->node.flags |= PM_UNSET;
}
#else
# error no gdbm
#endif /* have gdbm */
static struct features module_features = {
bintab, sizeof(bintab)/sizeof(*bintab),
NULL, 0,
NULL, 0,
NULL, 0,
0
};
/**/
int
setup_(UNUSED(Module m))
{
return 0;
}
/**/
int
features_(Module m, char ***features)
{
*features = featuresarray(m, &module_features);
return 0;
}
/**/
int
enables_(Module m, int **enables)
{
return handlefeatures(m, &module_features, enables);
}
/**/
int
boot_(UNUSED(Module m))
{
return 0;
}
/**/
int
cleanup_(Module m)
{
return setfeatureenables(m, &module_features, NULL);
}
/**/
int
finish_(UNUSED(Module m))
{
return 0;
}