1/*
2 * db_gdbm.c - bindings for gdbm
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 2008 Clint Adams
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Clint Adams or the Zsh Development
16 * Group be liable to any party for direct, indirect, special, incidental, or
17 * consequential damages arising out of the use of this software and its
18 * documentation, even if Peter Stephenson, Sven Wischnowsky and the Zsh
19 * Development Group have been advised of the possibility of such damage.
20 *
21 * Clint Adams and the Zsh Development Group
22 * specifically disclaim any warranties, including, but not limited to, the
23 * implied warranties of merchantability and fitness for a particular purpose.
24 * The software provided hereunder is on an "as is" basis, and Peter
25 * Stephenson, Sven Wischnowsky and the Zsh Development Group have no
26 * obligation to provide maintenance, support, updates, enhancements, or
27 * modifications.
28 *
29 */
30
31#include "db_gdbm.mdh"
32#include "db_gdbm.pro"
33
34/*
35 * Make sure we have all the bits I'm using for memory mapping, otherwise
36 * I don't know what I'm doing.
37 */
38#if defined(HAVE_GDBM_H) && defined(HAVE_GDBM_OPEN)
39
40#include <gdbm.h>
41
42#if 0 /* what is this for? */
43static char *backtype = "db/gdbm";
44#endif
45
46static const struct gsu_scalar gdbm_gsu =
47{ gdbmgetfn, gdbmsetfn, gdbmunsetfn };
48
49static struct builtin bintab[] = {
50    BUILTIN("ztie", 0, bin_ztie, 1, -1, 0, "d:f:", NULL),
51    BUILTIN("zuntie", 0, bin_zuntie, 1, -1, 0, NULL, NULL),
52};
53
54/**/
55static int
56bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
57{
58    char *resource_name, *pmname;
59    GDBM_FILE dbf = NULL;
60    Param tied_param;
61
62    if(!OPT_ISSET(ops,'d')) {
63        zwarnnam(nam, "you must pass `-d db/gdbm' to ztie", NULL);
64	return 1;
65    }
66    if(!OPT_ISSET(ops,'f')) {
67        zwarnnam(nam, "you must pass `-f' with a filename to ztie", NULL);
68	return 1;
69    }
70
71    /* Here should be a lookup of the backend type against
72     * a registry.
73     */
74
75    pmname = ztrdup(*args);
76
77    resource_name = OPT_ARG(ops, 'f');
78
79    if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys, 0))) {
80        zwarnnam(nam, "cannot create the requested parameter name", NULL);
81	return 1;
82    }
83
84    dbf = gdbm_open(resource_name, 0, GDBM_WRCREAT | GDBM_SYNC, 0666, 0);
85    if(!dbf) {
86        zwarnnam(nam, "error opening database file %s", resource_name);
87	return 1;
88    }
89
90    tied_param->u.hash->tmpdata = (void *)dbf;
91
92    return 0;
93}
94
95/**/
96static int
97bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
98{
99    Param pm;
100    GDBM_FILE dbf;
101
102    pm = (Param) paramtab->getnode(paramtab, args[0]);
103    if(!pm) {
104        zwarnnam(nam, "cannot untie %s", args[0]);
105	return 1;
106    }
107
108    dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
109    gdbm_close(dbf);
110/*    free(pm->u.hash->tmpdata); */
111    paramtab->removenode(paramtab, pm->node.nam);
112
113    return 0;
114}
115
116/**/
117static char *
118gdbmgetfn(Param pm)
119{
120    datum key, content;
121    int ret;
122    GDBM_FILE dbf;
123
124    key.dptr = pm->node.nam;
125    key.dsize = strlen(key.dptr) + 1;
126
127    dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
128    ret = gdbm_exists(dbf, key);
129    if(ret) {
130        content = gdbm_fetch(dbf, key);
131    } else {
132        content.dptr = dupstring("");
133    }
134
135    return content.dptr;
136}
137
138/**/
139static void
140gdbmsetfn(Param pm, char *val)
141{
142    datum key, content;
143    GDBM_FILE dbf;
144
145    key.dptr = pm->node.nam;
146    key.dsize = strlen(key.dptr) + 1;
147    content.dptr = val;
148    content.dsize = strlen(content.dptr) + 1;
149
150    dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
151    (void)gdbm_store(dbf, key, content, GDBM_REPLACE);
152}
153
154/**/
155static void
156gdbmunsetfn(Param pm, int um)
157{
158    datum key;
159    GDBM_FILE dbf;
160
161    key.dptr = pm->node.nam;
162    key.dsize = strlen(key.dptr) + 1;
163
164    dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
165    (void)gdbm_delete(dbf, key);
166}
167
168/**/
169static HashNode
170getgdbmnode(HashTable ht, const char *name)
171{
172    int len;
173    char *nameu;
174    Param pm = NULL;
175
176    nameu = dupstring(name);
177    unmetafy(nameu, &len);
178
179    pm = (Param) hcalloc(sizeof(struct param));
180    pm->node.nam = nameu;
181    pm->node.flags = PM_SCALAR;
182    pm->gsu.s = &gdbm_gsu;
183    pm->u.hash = ht;
184
185    return &pm->node;
186}
187
188/**/
189static void
190scangdbmkeys(HashTable ht, ScanFunc func, int flags)
191{
192    Param pm = NULL;
193    datum key, content;
194    GDBM_FILE dbf;
195
196    dbf = (GDBM_FILE)(ht->tmpdata);
197
198    pm = (Param) hcalloc(sizeof(struct param));
199
200    pm->node.flags = PM_SCALAR;
201    pm->gsu.s = &nullsetscalar_gsu;
202
203    key = gdbm_firstkey(dbf);
204
205    while(key.dptr) {
206	content = gdbm_fetch(dbf, key);
207
208	pm->node.nam = key.dptr;
209	pm->u.str = content.dptr;
210	pm->gsu.s = &nullsetscalar_gsu;
211
212	func(&pm->node, flags);
213
214        key = gdbm_nextkey(dbf, key);
215    }
216
217}
218
219#else
220# error no gdbm
221#endif /* have gdbm */
222
223static struct features module_features = {
224    bintab, sizeof(bintab)/sizeof(*bintab),
225    NULL, 0,
226    NULL, 0,
227    NULL, 0,
228    0
229};
230
231/**/
232int
233setup_(UNUSED(Module m))
234{
235    return 0;
236}
237
238/**/
239int
240features_(Module m, char ***features)
241{
242    *features = featuresarray(m, &module_features);
243    return 0;
244}
245
246/**/
247int
248enables_(Module m, int **enables)
249{
250    return handlefeatures(m, &module_features, enables);
251}
252
253/**/
254int
255boot_(UNUSED(Module m))
256{
257    return 0;
258}
259
260/**/
261int
262cleanup_(UNUSED(Module m))
263{
264    return setfeatureenables(m, &module_features, NULL);
265}
266
267/**/
268int
269finish_(UNUSED(Module m))
270{
271    return 0;
272}
273