1 /*************************************************************************************************
2  * Implementation of Hovel
3  *                                                      Copyright (C) 2000-2007 Mikio Hirabayashi
4  * This file is part of QDBM, Quick Database Manager.
5  * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * Lesser General Public License as published by the Free Software Foundation; either version
7  * 2.1 of the License or any later version.  QDBM is distributed in the hope that it will be
8  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
10  * details.
11  * You should have received a copy of the GNU Lesser General Public License along with QDBM; if
12  * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
13  * 02111-1307 USA.
14  *************************************************************************************************/
15 
16 
17 #define QDBM_INTERNAL  1
18 
19 #include "hovel.h"
20 #include "myconf.h"
21 
22 #define HV_INITBNUM    32749             /* initial bucket number */
23 #define HV_ALIGNSIZ    16                /* size of alignment */
24 
25 
26 /* private function prototypes */
27 static int gdbm_geterrno(int ecode);
28 
29 
30 
31 /*************************************************************************************************
32  * public objects
33  *************************************************************************************************/
34 
35 
36 /* String containing the version information. */
37 char *gdbm_version = "Powered by QDBM";
38 
39 
40 /* Get a message string corresponding to an error code. */
gdbm_strerror(gdbm_error gdbmerrno)41 char *gdbm_strerror(gdbm_error gdbmerrno){
42   switch(gdbmerrno){
43   case GDBM_NO_ERROR: return "No error";
44   case GDBM_MALLOC_ERROR: return "Malloc error";
45   case GDBM_BLOCK_SIZE_ERROR: return "Block size error";
46   case GDBM_FILE_OPEN_ERROR: return "File open error";
47   case GDBM_FILE_WRITE_ERROR: return "File write error";
48   case GDBM_FILE_SEEK_ERROR: return "File seek error";
49   case GDBM_FILE_READ_ERROR: return "File read error";
50   case GDBM_BAD_MAGIC_NUMBER: return "Bad magic number";
51   case GDBM_EMPTY_DATABASE: return "Empty database";
52   case GDBM_CANT_BE_READER: return "Can't be reader";
53   case GDBM_CANT_BE_WRITER: return "Can't be writer";
54   case GDBM_READER_CANT_DELETE: return "Reader can't delete";
55   case GDBM_READER_CANT_STORE: return "Reader can't store";
56   case GDBM_READER_CANT_REORGANIZE: return "Reader can't reorganize";
57   case GDBM_UNKNOWN_UPDATE: return "Unknown update";
58   case GDBM_ITEM_NOT_FOUND: return "Item not found";
59   case GDBM_REORGANIZE_FAILED: return "Reorganize failed";
60   case GDBM_CANNOT_REPLACE: return "Cannot replace";
61   case GDBM_ILLEGAL_DATA: return "Illegal data";
62   case GDBM_OPT_ALREADY_SET: return "Option already set";
63   case GDBM_OPT_ILLEGAL: return "Illegal option";
64   }
65   return "(invalid ecode)";
66 }
67 
68 
69 /* Get a database handle after the fashion of GDBM. */
gdbm_open(char * name,int block_size,int read_write,int mode,void (* fatal_func)(void))70 GDBM_FILE gdbm_open(char *name, int block_size, int read_write, int mode,
71                     void (*fatal_func)(void)){
72   GDBM_FILE dbf;
73   int dpomode;
74   DEPOT *depot;
75   int flags, fd;
76   assert(name);
77   dpomode = DP_OREADER;
78   flags = O_RDONLY;
79   if(read_write & GDBM_READER){
80     dpomode = GDBM_READER;
81     if(read_write & GDBM_NOLOCK) dpomode |= DP_ONOLCK;
82     if(read_write & GDBM_LOCKNB) dpomode |= DP_OLCKNB;
83     flags = O_RDONLY;
84   } else if(read_write & GDBM_WRITER){
85     dpomode = DP_OWRITER;
86     if(read_write & GDBM_NOLOCK) dpomode |= DP_ONOLCK;
87     if(read_write & GDBM_LOCKNB) dpomode |= DP_OLCKNB;
88     flags = O_RDWR;
89   } else if(read_write & GDBM_WRCREAT){
90     dpomode = DP_OWRITER | DP_OCREAT;
91     if(read_write & GDBM_NOLOCK) dpomode |= DP_ONOLCK;
92     if(read_write & GDBM_LOCKNB) dpomode |= DP_OLCKNB;
93     if(read_write & GDBM_SPARSE) dpomode |= DP_OSPARSE;
94     flags = O_RDWR | O_CREAT;
95   } else if(read_write & GDBM_NEWDB){
96     dpomode = DP_OWRITER | DP_OCREAT | DP_OTRUNC;
97     if(read_write & GDBM_NOLOCK) dpomode |= DP_ONOLCK;
98     if(read_write & GDBM_LOCKNB) dpomode |= DP_OLCKNB;
99     if(read_write & GDBM_SPARSE) dpomode |= DP_OSPARSE;
100     flags = O_RDWR | O_CREAT | O_TRUNC;
101   } else {
102     gdbm_errno = GDBM_ILLEGAL_DATA;
103     return NULL;
104   }
105   mode |= 00600;
106   if((fd = open(name, flags, mode)) == -1 || close(fd) == -1){
107     gdbm_errno = GDBM_FILE_OPEN_ERROR;
108     return NULL;
109   }
110   if(!(depot = dpopen(name, dpomode, HV_INITBNUM))){
111     gdbm_errno = gdbm_geterrno(dpecode);
112     if(dpecode == DP_ESTAT) gdbm_errno = GDBM_FILE_OPEN_ERROR;
113     return NULL;
114   }
115   if(dpomode & DP_OWRITER){
116     if(!dpsetalign(depot, HV_ALIGNSIZ)){
117       gdbm_errno = gdbm_geterrno(dpecode);
118       dpclose(depot);
119     }
120   }
121   if((dpomode & DP_OWRITER) && (read_write & GDBM_SYNC)){
122     if(!dpsync(depot)){
123       gdbm_errno = gdbm_geterrno(dpecode);
124       dpclose(depot);
125     }
126   }
127   if(!(dbf = malloc(sizeof(GDBM)))){
128     gdbm_errno = GDBM_MALLOC_ERROR;
129     dpclose(depot);
130     return NULL;
131   }
132   dbf->depot = depot;
133   dbf->curia = NULL;
134   dbf->syncmode = (dpomode & DP_OWRITER) && (read_write & GDBM_SYNC) ? TRUE : FALSE;
135   return dbf;
136 }
137 
138 
139 /* Get a database handle after the fashion of QDBM. */
gdbm_open2(char * name,int read_write,int mode,int bnum,int dnum,int align)140 GDBM_FILE gdbm_open2(char *name, int read_write, int mode, int bnum, int dnum, int align){
141   GDBM_FILE dbf;
142   int dpomode, cromode, flags, fd;
143   struct stat sbuf;
144   DEPOT *depot;
145   CURIA *curia;
146   assert(name);
147   dpomode = DP_OREADER;
148   cromode = CR_OREADER;
149   flags = O_RDONLY;
150   if(read_write & GDBM_READER){
151     dpomode = DP_OREADER;
152     cromode = CR_OREADER;
153     if(read_write & GDBM_NOLOCK){
154       dpomode |= DP_ONOLCK;
155       cromode |= CR_ONOLCK;
156     }
157     if(read_write & GDBM_LOCKNB){
158       dpomode |= DP_OLCKNB;
159       cromode |= CR_OLCKNB;
160     }
161     flags = O_RDONLY;
162   } else if(read_write & GDBM_WRITER){
163     dpomode = DP_OWRITER;
164     cromode = CR_OWRITER;
165     if(read_write & GDBM_NOLOCK){
166       dpomode |= DP_ONOLCK;
167       cromode |= CR_ONOLCK;
168     }
169     if(read_write & GDBM_LOCKNB){
170       dpomode |= DP_OLCKNB;
171       cromode |= CR_OLCKNB;
172     }
173     flags = O_RDWR;
174   } else if(read_write & GDBM_WRCREAT){
175     dpomode = DP_OWRITER | DP_OCREAT;
176     cromode = CR_OWRITER | CR_OCREAT;
177     if(read_write & GDBM_NOLOCK){
178       dpomode |= DP_ONOLCK;
179       cromode |= CR_ONOLCK;
180     }
181     if(read_write & GDBM_LOCKNB){
182       dpomode |= DP_OLCKNB;
183       cromode |= CR_OLCKNB;
184     }
185     if(read_write & GDBM_SPARSE){
186       dpomode |= DP_OSPARSE;
187       cromode |= CR_OSPARSE;
188     }
189     flags = O_RDWR | O_CREAT;
190   } else if(read_write & GDBM_NEWDB){
191     dpomode = DP_OWRITER | DP_OCREAT | DP_OTRUNC;
192     cromode = CR_OWRITER | CR_OCREAT | CR_OTRUNC;
193     if(read_write & GDBM_NOLOCK){
194       dpomode |= DP_ONOLCK;
195       cromode |= CR_ONOLCK;
196     }
197     if(read_write & GDBM_LOCKNB){
198       dpomode |= DP_OLCKNB;
199       cromode |= CR_OLCKNB;
200     }
201     if(read_write & GDBM_SPARSE){
202       dpomode |= DP_OSPARSE;
203       cromode |= CR_OSPARSE;
204     }
205     flags = O_RDWR | O_CREAT | O_TRUNC;
206   } else {
207     gdbm_errno = GDBM_ILLEGAL_DATA;
208     return NULL;
209   }
210   if(lstat(name, &sbuf) != -1){
211     if(S_ISDIR(sbuf.st_mode)){
212       if(dnum < 1) dnum = 1;
213     } else {
214       dnum = 0;
215     }
216   }
217   depot = NULL;
218   curia = NULL;
219   if(dnum > 0){
220     mode |= 00700;
221     if((cromode & CR_OCREAT)){
222       if(mkdir(name, mode) == -1 && errno != EEXIST){
223         gdbm_errno = GDBM_FILE_OPEN_ERROR;
224         return NULL;
225       }
226     }
227     if(!(curia = cropen(name, cromode, bnum, dnum))){
228       gdbm_errno = gdbm_geterrno(dpecode);
229       return NULL;
230     }
231     if(cromode & CR_OWRITER) crsetalign(curia, align);
232     if((cromode & CR_OWRITER) && (read_write & GDBM_SYNC)) crsync(curia);
233   } else {
234     mode |= 00600;
235     if(dpomode & DP_OWRITER){
236       if((fd = open(name, flags, mode)) == -1 || close(fd) == -1){
237         gdbm_errno = GDBM_FILE_OPEN_ERROR;
238         return NULL;
239       }
240     }
241     if(!(depot = dpopen(name, dpomode, bnum))){
242       gdbm_errno = gdbm_geterrno(dpecode);
243       return NULL;
244     }
245     if(dpomode & DP_OWRITER) dpsetalign(depot, align);
246     if((dpomode & DP_OWRITER) && (read_write & GDBM_SYNC)) dpsync(depot);
247   }
248   if(!(dbf = malloc(sizeof(GDBM)))){
249     gdbm_errno = GDBM_MALLOC_ERROR;
250     if(depot) dpclose(depot);
251     if(curia) crclose(curia);
252     return NULL;
253   }
254   dbf->depot = depot;
255   dbf->curia = curia;
256   dbf->syncmode = (dpomode & DP_OWRITER) && (read_write & GDBM_SYNC) ? TRUE : FALSE;
257   return dbf;
258 }
259 
260 
261 /* Close a database handle. */
gdbm_close(GDBM_FILE dbf)262 void gdbm_close(GDBM_FILE dbf){
263   assert(dbf);
264   if(dbf->depot){
265     if(dbf->syncmode) dpsync(dbf->depot);
266     dpclose(dbf->depot);
267   } else {
268     if(dbf->syncmode) crsync(dbf->curia);
269     crclose(dbf->curia);
270   }
271   free(dbf);
272 }
273 
274 
275 /* Store a record. */
gdbm_store(GDBM_FILE dbf,datum key,datum content,int flag)276 int gdbm_store(GDBM_FILE dbf, datum key, datum content, int flag){
277   int dmode;
278   assert(dbf);
279   if(!key.dptr || key.dsize < 0 || !content.dptr || content.dsize < 0){
280     gdbm_errno = GDBM_ILLEGAL_DATA;
281     return -1;
282   }
283   if(dbf->depot){
284     if(!dpwritable(dbf->depot)){
285       gdbm_errno = GDBM_READER_CANT_STORE;
286       return -1;
287     }
288     dmode = (flag == GDBM_INSERT) ? DP_DKEEP : DP_DOVER;
289     if(!dpput(dbf->depot, key.dptr, key.dsize, content.dptr, content.dsize, dmode)){
290       gdbm_errno = gdbm_geterrno(dpecode);
291       if(dpecode == DP_EKEEP) return 1;
292       return -1;
293     }
294     if(dbf->syncmode && !dpsync(dbf->depot)){
295       gdbm_errno = gdbm_geterrno(dpecode);
296       return -1;
297     }
298   } else {
299     if(!crwritable(dbf->curia)){
300       gdbm_errno = GDBM_READER_CANT_STORE;
301       return -1;
302     }
303     dmode = (flag == GDBM_INSERT) ? CR_DKEEP : CR_DOVER;
304     if(!crput(dbf->curia, key.dptr, key.dsize, content.dptr, content.dsize, dmode)){
305       gdbm_errno = gdbm_geterrno(dpecode);
306       if(dpecode == DP_EKEEP) return 1;
307       return -1;
308     }
309     if(dbf->syncmode && !crsync(dbf->curia)){
310       gdbm_errno = gdbm_geterrno(dpecode);
311       return -1;
312     }
313   }
314   return 0;
315 }
316 
317 
318 /* Delete a record. */
gdbm_delete(GDBM_FILE dbf,datum key)319 int gdbm_delete(GDBM_FILE dbf, datum key){
320   assert(dbf);
321   if(!key.dptr || key.dsize < 0){
322     gdbm_errno = GDBM_ILLEGAL_DATA;
323     return -1;
324   }
325   if(dbf->depot){
326     if(!dpwritable(dbf->depot)){
327       gdbm_errno = GDBM_READER_CANT_DELETE;
328       return -1;
329     }
330     if(!dpout(dbf->depot, key.dptr, key.dsize)){
331       gdbm_errno = gdbm_geterrno(dpecode);
332       return -1;
333     }
334     if(dbf->syncmode && !dpsync(dbf->depot)){
335       gdbm_errno = gdbm_geterrno(dpecode);
336       return -1;
337     }
338   } else {
339     if(!crwritable(dbf->curia)){
340       gdbm_errno = GDBM_READER_CANT_DELETE;
341       return -1;
342     }
343     if(!crout(dbf->curia, key.dptr, key.dsize)){
344       gdbm_errno = gdbm_geterrno(dpecode);
345       return -1;
346     }
347     if(dbf->syncmode && !crsync(dbf->curia)){
348       gdbm_errno = gdbm_geterrno(dpecode);
349       return -1;
350     }
351   }
352   return 0;
353 }
354 
355 
356 /* Retrieve a record. */
gdbm_fetch(GDBM_FILE dbf,datum key)357 datum gdbm_fetch(GDBM_FILE dbf, datum key){
358   datum content;
359   char *vbuf;
360   int vsiz;
361   assert(dbf);
362   if(!key.dptr || key.dsize < 0){
363     gdbm_errno = GDBM_ILLEGAL_DATA;
364     content.dptr = NULL;
365     content.dsize = 0;
366     return content;
367   }
368   if(dbf->depot){
369     if(!(vbuf = dpget(dbf->depot, key.dptr, key.dsize, 0, -1, &vsiz))){
370       gdbm_errno = gdbm_geterrno(dpecode);
371       content.dptr = NULL;
372       content.dsize = 0;
373       return content;
374     }
375   } else {
376     if(!(vbuf = crget(dbf->curia, key.dptr, key.dsize, 0, -1, &vsiz))){
377       gdbm_errno = gdbm_geterrno(dpecode);
378       content.dptr = NULL;
379       content.dsize = 0;
380       return content;
381     }
382   }
383   content.dptr = vbuf;
384   content.dsize = vsiz;
385   return content;
386 }
387 
388 
389 /* Check whether a record exists or not. */
gdbm_exists(GDBM_FILE dbf,datum key)390 int gdbm_exists(GDBM_FILE dbf, datum key){
391   assert(dbf);
392   if(!key.dptr || key.dsize < 0){
393     gdbm_errno = GDBM_ILLEGAL_DATA;
394     return FALSE;
395   }
396   if(dbf->depot){
397     if(dpvsiz(dbf->depot, key.dptr, key.dsize) == -1){
398       gdbm_errno = gdbm_geterrno(dpecode);
399       return FALSE;
400     }
401   } else {
402     if(crvsiz(dbf->curia, key.dptr, key.dsize) == -1){
403       gdbm_errno = gdbm_geterrno(dpecode);
404       return FALSE;
405     }
406   }
407   return TRUE;
408 }
409 
410 
411 /* Get the first key of a database. */
gdbm_firstkey(GDBM_FILE dbf)412 datum gdbm_firstkey(GDBM_FILE dbf){
413   datum key;
414   assert(dbf);
415   memset(&key, 0, sizeof(datum));
416   if(dbf->depot){
417     if(dprnum(dbf->depot) < 1){
418       gdbm_errno = GDBM_EMPTY_DATABASE;
419       key.dptr = NULL;
420       key.dsize = 0;
421       return key;
422     }
423     dpiterinit(dbf->depot);
424     return gdbm_nextkey(dbf, key);
425   } else {
426     if(crrnum(dbf->curia) < 1){
427       gdbm_errno = GDBM_EMPTY_DATABASE;
428       key.dptr = NULL;
429       key.dsize = 0;
430       return key;
431     }
432     criterinit(dbf->curia);
433     return gdbm_nextkey(dbf, key);
434   }
435 }
436 
437 
438 /* Get the next key of a database. */
gdbm_nextkey(GDBM_FILE dbf,datum key)439 datum gdbm_nextkey(GDBM_FILE dbf, datum key){
440   char *kbuf;
441   int ksiz;
442   assert(dbf);
443   if(dbf->depot){
444     if(!(kbuf = dpiternext(dbf->depot, &ksiz))){
445       gdbm_errno = gdbm_geterrno(dpecode);
446       key.dptr = NULL;
447       key.dsize = 0;
448       return key;
449     }
450   } else {
451     if(!(kbuf = criternext(dbf->curia, &ksiz))){
452       gdbm_errno = gdbm_geterrno(dpecode);
453       key.dptr = NULL;
454       key.dsize = 0;
455       return key;
456     }
457   }
458   key.dptr = kbuf;
459   key.dsize = ksiz;
460   return key;
461 }
462 
463 
464 /* Synchronize contents of updating with the file and the device. */
gdbm_sync(GDBM_FILE dbf)465 void gdbm_sync(GDBM_FILE dbf){
466   assert(dbf);
467   if(dbf->depot){
468     if(!dpsync(dbf->depot)) gdbm_errno = gdbm_geterrno(dpecode);
469   } else {
470     if(!crsync(dbf->curia)) gdbm_errno = gdbm_geterrno(dpecode);
471   }
472 }
473 
474 
475 /* Reorganize a database. */
gdbm_reorganize(GDBM_FILE dbf)476 int gdbm_reorganize(GDBM_FILE dbf){
477   assert(dbf);
478   if(dbf->depot){
479     if(!dpwritable(dbf->depot)){
480       gdbm_errno = GDBM_READER_CANT_REORGANIZE;
481       return -1;
482     }
483     if(!dpoptimize(dbf->depot, dprnum(dbf->depot) >= HV_INITBNUM ? -1 : HV_INITBNUM)){
484       gdbm_errno = gdbm_geterrno(dpecode);
485       return -1;
486     }
487   } else {
488     if(!crwritable(dbf->curia)){
489       gdbm_errno = GDBM_READER_CANT_REORGANIZE;
490       return -1;
491     }
492     if(!croptimize(dbf->curia, crrnum(dbf->curia) >= HV_INITBNUM ? -1 : HV_INITBNUM)){
493       gdbm_errno = gdbm_geterrno(dpecode);
494       return -1;
495     }
496   }
497   return 0;
498 }
499 
500 
501 /* Get the file descriptor of a database file. */
gdbm_fdesc(GDBM_FILE dbf)502 int gdbm_fdesc(GDBM_FILE dbf){
503   assert(dbf);
504   if(dbf->depot){
505     return dpfdesc(dbf->depot);
506   } else {
507     return -1;
508   }
509 }
510 
511 
512 /* No effect. */
gdbm_setopt(GDBM_FILE dbf,int option,int * value,int size)513 int gdbm_setopt(GDBM_FILE dbf, int option, int *value, int size){
514   assert(dbf);
515   return 0;
516 }
517 
518 
519 
520 /*************************************************************************************************
521  * features for experts
522  *************************************************************************************************/
523 
524 
525 /* Get the pointer of the last happened error code. */
gdbm_errnoptr(void)526 int *gdbm_errnoptr(void){
527   static int deferrno = GDBM_NO_ERROR;
528   void *ptr;
529   if(_qdbm_ptsafe){
530     if(!(ptr = _qdbm_settsd(&deferrno, sizeof(int), &deferrno))){
531       deferrno = GDBM_ILLEGAL_DATA;
532       return &deferrno;
533     }
534     return (int *)ptr;
535   }
536   return &deferrno;
537 }
538 
539 
540 
541 /*************************************************************************************************
542  * private objects
543  *************************************************************************************************/
544 
545 
546 /* Get the error code of GDBM corresponding to an error code of Depot.
547    `ecode' specifies an error code of Depot.
548    The return value is the error code of GDBM. */
gdbm_geterrno(int ecode)549 static int gdbm_geterrno(int ecode){
550   switch(ecode){
551   case DP_ENOERR: return GDBM_NO_ERROR;
552   case DP_EBROKEN: return GDBM_BAD_MAGIC_NUMBER;
553   case DP_EKEEP: return GDBM_CANNOT_REPLACE;
554   case DP_ENOITEM: return GDBM_ITEM_NOT_FOUND;
555   case DP_EALLOC: return GDBM_MALLOC_ERROR;
556   case DP_EOPEN: return GDBM_FILE_OPEN_ERROR;
557   case DP_ESEEK: return GDBM_FILE_SEEK_ERROR;
558   case DP_EREAD: return GDBM_FILE_READ_ERROR;
559   case DP_EWRITE: return GDBM_FILE_WRITE_ERROR;
560   case DP_EMKDIR: return GDBM_FILE_OPEN_ERROR;
561   default: break;
562   }
563   return GDBM_ILLEGAL_DATA;
564 }
565 
566 
567 
568 /* END OF FILE */
569