1 /*
2 * Make our db functions a wrapper for GDBM.
3 *
4 * Copyright 2007 Andrew Wood, distributed under the Artistic License.
5 */
6
7 #include "config.h"
8 #include "database.h"
9
10 #ifdef USING_GDBM
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #ifdef HAVE_FCNTL
18 #include <fcntl.h>
19 #endif
20 #include <unistd.h>
21 #include <gdbm.h>
22
23 struct qdbint_s {
24 GDBM_FILE dbf;
25 #ifndef HAVE_GDBM_FDESC
26 int fd;
27 #endif /* !HAVE_GDBM_FDESC */
28 };
29
30
31 /*
32 * Return a file descriptor for the given database, or -1 on error.
33 */
qdb_gdbm_fd(qdbint_t db)34 int qdb_gdbm_fd(qdbint_t db)
35 {
36 if (db == NULL)
37 return -1;
38 if (db->dbf == NULL)
39 return -1;
40 #ifdef HAVE_GDBM_FDESC
41 return gdbm_fdesc(db->dbf);
42 #else /* !HAVE_GDBM_FDESC */
43 return db->fd;
44 #endif /* HAVE_GDBM_FDESC */
45 }
46
47
48 /*
49 * Return nonzero if the given file is of this database type.
50 */
qdb_gdbm_identify(const char * file)51 int qdb_gdbm_identify(const char *file)
52 {
53 FILE *fptr;
54 unsigned char buf[8]; /* RATS: ignore (checked) */
55
56 if (file == NULL)
57 return 0;
58 if (strncasecmp(file, "gdbm:", 5) == 0)
59 return 1;
60
61 fptr = fopen(file, "rb");
62 if (fptr == NULL)
63 return 0;
64 if (fread(buf, 4, 1, fptr) < 1) {
65 fclose(fptr);
66 return 0;
67 }
68
69 fclose(fptr);
70
71 buf[4] = 0;
72
73 if ((buf[0] == 0x13)
74 && (buf[1] == 0x57)
75 && (buf[2] == 0x9a)
76 && (buf[3] == 0xce)
77 )
78 return 1;
79
80 if ((buf[3] == 0x13)
81 && (buf[2] == 0x57)
82 && (buf[1] == 0x9a)
83 && (buf[0] == 0xce)
84 )
85 return 1;
86
87 if (strncmp((char *) buf, "GDBM", 4) == 0)
88 return 1;
89
90 return 0;
91 }
92
93
94 /*
95 * Open the given database in the given way (new database, read-only, or
96 * read-write); return a qdbint_t or NULL on error.
97 */
qdb_gdbm_open(const char * file,qdb_open_t method)98 qdbint_t qdb_gdbm_open(const char *file, qdb_open_t method)
99 {
100 GDBM_FILE dbf = NULL;
101 qdbint_t db;
102 int tries;
103
104 if (strncasecmp(file, "gdbm:", 5) == 0)
105 file += 5;
106
107 for (tries = 0; tries < 60 && dbf == NULL; tries++) {
108 switch (method) {
109 case QDB_NEW:
110 dbf =
111 gdbm_open((char *) file, 512, GDBM_NEWDB,
112 S_IRUSR | S_IWUSR, NULL);
113 break;
114 case QDB_READONLY:
115 dbf =
116 gdbm_open((char *) file, 512, GDBM_READER,
117 S_IRUSR, NULL);
118 break;
119 case QDB_READWRITE:
120 dbf =
121 gdbm_open((char *) file, 512, GDBM_WRCREAT,
122 S_IRUSR | S_IWUSR, NULL);
123 break;
124 default:
125 break;
126 }
127
128 if (dbf != NULL)
129 break;
130
131 switch (gdbm_errno) {
132 case GDBM_CANT_BE_READER:
133 case GDBM_CANT_BE_WRITER:
134 sleep(1);
135 break;
136 default:
137 return NULL;
138 }
139 }
140
141 if (dbf == NULL)
142 return NULL;
143
144 db = calloc(1, sizeof(*db));
145 if (db == NULL) {
146 gdbm_close(dbf);
147 return NULL;
148 }
149 #ifndef HAVE_GDBM_FDESC
150 db->fd = open(file, O_RDONLY);
151 #endif /* !HAVE_GDBM_FDESC */
152
153 db->dbf = dbf;
154 return db;
155 }
156
157
158 /*
159 * Close the given database.
160 */
qdb_gdbm_close(qdbint_t db)161 void qdb_gdbm_close(qdbint_t db)
162 {
163 if (db == NULL)
164 return;
165 #ifndef HAVE_GDBM_FDESC
166 if (db->fd >= 0)
167 close(db->fd);
168 #endif /* !HAVE_GDBM_FDESC */
169 gdbm_close(db->dbf);
170 free(db);
171 }
172
173
174 /*
175 * Fetch a value from the database. The datum returned needs its val.data
176 * free()ing after use. If val.data is NULL, no value was found for the
177 * given key.
178 */
qdb_gdbm_fetch(qdbint_t db,qdb_datum key)179 qdb_datum qdb_gdbm_fetch(qdbint_t db, qdb_datum key)
180 {
181 datum gkey, gval;
182 qdb_datum val;
183
184 if (db == NULL) {
185 val.data = NULL;
186 val.size = 0;
187 return val;
188 }
189
190 if (key.data == NULL) {
191 val.data = NULL;
192 val.size = 0;
193 return val;
194 }
195
196 gkey.dptr = (char *) (key.data);
197 gkey.dsize = key.size;
198
199 gval = gdbm_fetch(db->dbf, gkey);
200
201 val.data = (unsigned char *) (gval.dptr);
202 val.size = gval.dsize;
203
204 return val;
205 }
206
207
208 /*
209 * Store the given key with the given value into the database, replacing any
210 * existing value for that key. Returns nonzero on error.
211 */
qdb_gdbm_store(qdbint_t db,qdb_datum key,qdb_datum val)212 int qdb_gdbm_store(qdbint_t db, qdb_datum key, qdb_datum val)
213 {
214 datum gkey, gval;
215
216 if (db == NULL)
217 return 1;
218
219 if ((key.data == NULL) || (val.data == NULL))
220 return 1;
221
222 gkey.dptr = (char *) (key.data);
223 gkey.dsize = key.size;
224 gval.dptr = (char *) (val.data);
225 gval.dsize = val.size;
226
227 return gdbm_store(db->dbf, gkey, gval, GDBM_REPLACE);
228 }
229
230
231 /*
232 * Delete the given key from the database. Returns nonzero on error.
233 */
qdb_gdbm_delete(qdbint_t db,qdb_datum key)234 int qdb_gdbm_delete(qdbint_t db, qdb_datum key)
235 {
236 datum gkey;
237
238 if (db == NULL)
239 return 1;
240
241 if (key.data == NULL)
242 return 1;
243
244 gkey.dptr = (char *) (key.data);
245 gkey.dsize = key.size;
246
247 return gdbm_delete(db->dbf, gkey);
248 }
249
250
251 /*
252 * Return the "first" key in the database, suitable for using with repeated
253 * calls to qdb_nextkey() to walk through every key in the database.
254 */
qdb_gdbm_firstkey(qdbint_t db)255 qdb_datum qdb_gdbm_firstkey(qdbint_t db)
256 {
257 datum gkey;
258 qdb_datum key;
259
260 gkey = gdbm_firstkey(db->dbf);
261
262 key.data = (unsigned char *) (gkey.dptr);
263 key.size = gkey.dsize;
264
265 return key;
266 }
267
268
269 /*
270 * Return the "next" key in the database, or key.data=NULL when all keys
271 * have been returned.
272 */
qdb_gdbm_nextkey(qdbint_t db,qdb_datum key)273 qdb_datum qdb_gdbm_nextkey(qdbint_t db, qdb_datum key)
274 {
275 datum gkey, newgkey;
276 qdb_datum newkey;
277
278 gkey.dptr = (char *) (key.data);
279 gkey.dsize = key.size;
280
281 newgkey = gdbm_nextkey(db->dbf, gkey);
282
283 newkey.data = (unsigned char *) (newgkey.dptr);
284 newkey.size = newgkey.dsize;
285
286 return newkey;
287 }
288
289
290 /*
291 * Reorganise the database for better efficiency.
292 */
qdb_gdbm_optimise(qdbint_t db)293 void qdb_gdbm_optimise(qdbint_t db)
294 {
295 gdbm_reorganize(db->dbf);
296 }
297
298
299 /*
300 * Return a string describing the last database error to occur.
301 */
qdb_gdbm_error(void)302 char *qdb_gdbm_error(void)
303 {
304 return (char *) gdbm_strerror(gdbm_errno);
305 }
306
307
308 #endif /* USING_GDBM */
309
310 /* EOF */
311