/* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)ndbm.c 5.3 (Berkeley) 3/9/86"; #endif LIBC_SCCS and not lint #include #include #include #include #include #include #define BYTESIZ 8 #undef setbit static datum makdatum(); static long hashinc(); static long dcalchash(); extern int errno; DBM * dbm_open(file, flags, mode) char *file; int flags, mode; { struct stat statb; register DBM *db; if ((db = (DBM *)malloc(sizeof *db)) == 0) { errno = ENOMEM; return ((DBM *)0); } db->dbm_flags = (flags & 03) == O_RDONLY ? _DBM_RDONLY : 0; if ((flags & 03) == O_WRONLY) flags = (flags & ~03) | O_RDWR; strcpy(db->dbm_pagbuf, file); strcat(db->dbm_pagbuf, ".pag"); db->dbm_pagf = open(db->dbm_pagbuf, flags, mode); if (db->dbm_pagf < 0) goto bad; strcpy(db->dbm_pagbuf, file); strcat(db->dbm_pagbuf, ".dir"); db->dbm_dirf = open(db->dbm_pagbuf, flags, mode); if (db->dbm_dirf < 0) goto bad1; fstat(db->dbm_dirf, &statb); db->dbm_maxbno = statb.st_size*BYTESIZ-1; db->dbm_pagbno = db->dbm_dirbno = -1; return (db); bad1: (void) close(db->dbm_pagf); bad: free((char *)db); return ((DBM *)0); } void dbm_close(db) DBM *db; { (void) close(db->dbm_dirf); (void) close(db->dbm_pagf); free((char *)db); } long dbm_forder(db, key) register DBM *db; datum key; { long hash; hash = dcalchash(key); for (db->dbm_hmask=0;; db->dbm_hmask=(db->dbm_hmask<<1)+1) { db->dbm_blkno = hash & db->dbm_hmask; db->dbm_bitno = db->dbm_blkno + db->dbm_hmask; if (getbit(db) == 0) break; } return (db->dbm_blkno); } datum dbm_fetch(db, key) register DBM *db; datum key; { register i; datum item; if (dbm_error(db)) goto err; dbm_access(db, dcalchash(key)); if ((i = finddatum(db->dbm_pagbuf, key)) >= 0) { item = makdatum(db->dbm_pagbuf, i+1); if (item.dptr != NULL) return (item); } err: item.dptr = NULL; item.dsize = 0; return (item); } dbm_delete(db, key) register DBM *db; datum key; { register i; datum item; if (dbm_error(db)) return (-1); if (dbm_rdonly(db)) { errno = EPERM; return (-1); } dbm_access(db, dcalchash(key)); if ((i = finddatum(db->dbm_pagbuf, key)) < 0) return (-1); if (!delitem(db->dbm_pagbuf, i)) goto err; db->dbm_pagbno = db->dbm_blkno; (void) lseek(db->dbm_pagf, db->dbm_blkno*PBLKSIZ, L_SET); if (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) { err: db->dbm_flags |= _DBM_IOERR; return (-1); } return (0); } dbm_store(db, key, dat, replace) register DBM *db; datum key, dat; int replace; { register i; datum item, item1; char ovfbuf[PBLKSIZ]; if (dbm_error(db)) return (-1); if (dbm_rdonly(db)) { errno = EPERM; return (-1); } loop: dbm_access(db, dcalchash(key)); if ((i = finddatum(db->dbm_pagbuf, key)) >= 0) { if (!replace) return (1); if (!delitem(db->dbm_pagbuf, i)) { db->dbm_flags |= _DBM_IOERR; return (-1); } } if (!additem(db->dbm_pagbuf, key, dat)) goto split; db->dbm_pagbno = db->dbm_blkno; (void) lseek(db->dbm_pagf, db->dbm_blkno*PBLKSIZ, L_SET); if (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) { db->dbm_flags |= _DBM_IOERR; return (-1); } return (0); split: if (key.dsize+dat.dsize+3*sizeof(short) >= PBLKSIZ) { db->dbm_flags |= _DBM_IOERR; errno = ENOSPC; return (-1); } bzero(ovfbuf, PBLKSIZ); for (i=0;;) { item = makdatum(db->dbm_pagbuf, i); if (item.dptr == NULL) break; if (dcalchash(item) & (db->dbm_hmask+1)) { item1 = makdatum(db->dbm_pagbuf, i+1); if (item1.dptr == NULL) { fprintf(stderr, "ndbm: split not paired\n"); db->dbm_flags |= _DBM_IOERR; break; } if (!additem(ovfbuf, item, item1) || !delitem(db->dbm_pagbuf, i)) { db->dbm_flags |= _DBM_IOERR; return (-1); } continue; } i += 2; } db->dbm_pagbno = db->dbm_blkno; (void) lseek(db->dbm_pagf, db->dbm_blkno*PBLKSIZ, L_SET); if (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) { db->dbm_flags |= _DBM_IOERR; return (-1); } (void) lseek(db->dbm_pagf, (db->dbm_blkno+db->dbm_hmask+1)*PBLKSIZ, L_SET); if (write(db->dbm_pagf, ovfbuf, PBLKSIZ) != PBLKSIZ) { db->dbm_flags |= _DBM_IOERR; return (-1); } setbit(db); goto loop; } datum dbm_firstkey(db) DBM *db; { db->dbm_blkptr = 0L; db->dbm_keyptr = 0; return (dbm_nextkey(db)); } datum dbm_nextkey(db) register DBM *db; { struct stat statb; datum item; if (dbm_error(db) || fstat(db->dbm_pagf, &statb) < 0) goto err; statb.st_size /= PBLKSIZ; for (;;) { if (db->dbm_blkptr != db->dbm_pagbno) { db->dbm_pagbno = db->dbm_blkptr; (void) lseek(db->dbm_pagf, db->dbm_blkptr*PBLKSIZ, L_SET); if (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) bzero(db->dbm_pagbuf, PBLKSIZ); #ifdef DEBUG else if (chkblk(db->dbm_pagbuf) < 0) db->dbm_flags |= _DBM_IOERR; #endif } if (((short *)db->dbm_pagbuf)[0] != 0) { item = makdatum(db->dbm_pagbuf, db->dbm_keyptr); if (item.dptr != NULL) { db->dbm_keyptr += 2; return (item); } db->dbm_keyptr = 0; } if (++db->dbm_blkptr >= statb.st_size) break; } err: item.dptr = NULL; item.dsize = 0; return (item); } static dbm_access(db, hash) register DBM *db; long hash; { for (db->dbm_hmask=0;; db->dbm_hmask=(db->dbm_hmask<<1)+1) { db->dbm_blkno = hash & db->dbm_hmask; db->dbm_bitno = db->dbm_blkno + db->dbm_hmask; if (getbit(db) == 0) break; } if (db->dbm_blkno != db->dbm_pagbno) { db->dbm_pagbno = db->dbm_blkno; (void) lseek(db->dbm_pagf, db->dbm_blkno*PBLKSIZ, L_SET); if (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) bzero(db->dbm_pagbuf, PBLKSIZ); #ifdef DEBUG else if (chkblk(db->dbm_pagbuf) < 0) db->dbm_flags |= _DBM_IOERR; #endif } } static getbit(db) register DBM *db; { long bn, b; register i, n; if (db->dbm_bitno > db->dbm_maxbno) return (0); n = db->dbm_bitno % BYTESIZ; bn = db->dbm_bitno / BYTESIZ; i = bn % DBLKSIZ; b = bn / DBLKSIZ; if (b != db->dbm_dirbno) { db->dbm_dirbno = b; (void) lseek(db->dbm_dirf, b*DBLKSIZ, L_SET); if (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ) bzero(db->dbm_dirbuf, DBLKSIZ); } return (db->dbm_dirbuf[i] & (1<dbm_bitno > db->dbm_maxbno) db->dbm_maxbno = db->dbm_bitno; n = db->dbm_bitno % BYTESIZ; bn = db->dbm_bitno / BYTESIZ; i = bn % DBLKSIZ; b = bn / DBLKSIZ; if (b != db->dbm_dirbno) { db->dbm_dirbno = b; (void) lseek(db->dbm_dirf, b*DBLKSIZ, L_SET); if (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ) bzero(db->dbm_dirbuf, DBLKSIZ); } db->dbm_dirbuf[i] |= 1<dbm_dirbno = b; (void) lseek(db->dbm_dirf, (long)b*DBLKSIZ, L_SET); if (write(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ) db->dbm_flags |= _DBM_IOERR; } static datum makdatum(buf, n) char buf[PBLKSIZ]; { register short *sp; register t; datum item; sp = (short *)buf; if ((unsigned)n >= sp[0]) { item.dptr = NULL; item.dsize = 0; return (item); } t = PBLKSIZ; if (n > 0) t = sp[n]; item.dptr = buf+sp[n+1]; item.dsize = t - sp[n+1]; return (item); } static finddatum(buf, item) char buf[PBLKSIZ]; datum item; { register short *sp; register int i, n, j; sp = (short *)buf; n = PBLKSIZ; for (i=0, j=sp[0]; idbm_hmask; bit = db->dbm_hmask+1; for (;;) { bit >>= 1; if (bit == 0) return (0L); if ((hash & bit) == 0) return (hash | bit); hash &= ~bit; } } static long dcalchash(item) datum item; { register int s, c, j; register char *cp; register long hashl; register int hashi; hashl = 0; hashi = 0; for (cp = item.dptr, s=item.dsize; --s >= 0; ) { c = *cp++; for (j=0; j>= 4; } } return (hashl); } /* * Delete pairs of items (n & n+1). */ static delitem(buf, n) char buf[PBLKSIZ]; { register short *sp, *sp1; register i1, i2; sp = (short *)buf; i2 = sp[0]; if ((unsigned)n >= i2 || (n & 1)) return (0); if (n == i2-2) { sp[0] -= 2; return (1); } i1 = PBLKSIZ; if (n > 0) i1 = sp[n]; i1 -= sp[n+2]; if (i1 > 0) { i2 = sp[i2]; bcopy(&buf[i2], &buf[i2 + i1], sp[n+2] - i2); } sp[0] -= 2; for (sp1 = sp + sp[0], sp += n+1; sp <= sp1; sp++) sp[0] = sp[2] + i1; return (1); } /* * Add pairs of items (item & item1). */ static additem(buf, item, item1) char buf[PBLKSIZ]; datum item, item1; { register short *sp; register i1, i2; sp = (short *)buf; i1 = PBLKSIZ; i2 = sp[0]; if (i2 > 0) i1 = sp[i2]; i1 -= item.dsize + item1.dsize; if (i1 <= (int)((i2+3) * sizeof(short))) return (0); sp[0] += 2; sp[++i2] = i1 + item1.dsize; bcopy(item.dptr, &buf[i1 + item1.dsize], item.dsize); sp[++i2] = i1; bcopy(item1.dptr, &buf[i1], item1.dsize); return (1); } #ifdef DEBUG static chkblk(buf) char buf[PBLKSIZ]; { register short *sp; register t, i; sp = (short *)buf; t = PBLKSIZ; for (i=0; i t) return (-1); t = sp[i+1]; } if (t < (sp[0]+1)*sizeof(short)) return (-1); return (0); } #endif