ndbm.c revision 722:636b850d4ee9
1353358Sdim/*
2353358Sdim * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
3353358Sdim * Use is subject to license terms.
4276789Sdim */
5276789Sdim
6276789Sdim/*
7276789Sdim * Copyright (c) 1983 Regents of the University of California.
8276789Sdim * All rights reserved.  The Berkeley software License Agreement
9276789Sdim * specifies the terms and conditions for redistribution.
10276789Sdim */
11276789Sdim
12276789Sdim#pragma ident	"%Z%%M%	%I%	%E% SMI"
13276789Sdim
14276789Sdim#include <sys/types.h>
15276789Sdim#include <sys/stat.h>
16276789Sdim#include <sys/file.h>
17276789Sdim#include <stdio.h>
18276789Sdim#include <errno.h>
19276789Sdim#include <ndbm.h>
20276789Sdim#include <stdlib.h>
21276789Sdim#include <string.h>
22276789Sdim
23276789Sdimdatum dbm_do_nextkey(DBM *, datum);
24276789Sdim
25276789Sdim
26276789Sdim/*add support for batched writing for NIS*/
27276789Sdim
28276789Sdim#define _DBM_DEFWRITE 0x4
29276789Sdim#define _DBM_DIRTY 0x8
30276789Sdim#define _DBM_DIRDIRTY 0x10
31276789Sdim#define dbm_dirty(db) ((db)->dbm_flags & _DBM_DIRTY)
32276789Sdim#define dbm_dirdirty(db) ((db)->dbm_flags & _DBM_DIRDIRTY)
33276789Sdim#define dbm_defwrite(db) ((db)->dbm_flags & _DBM_DEFWRITE)
34276789Sdim#define dbm_setdirty(db) (db)->dbm_flags |= _DBM_DIRTY
35276789Sdim#define dbm_clrdirty(db) (db)->dbm_flags &= ~_DBM_DIRTY
36276789Sdim#define dbm_setdirdirty(db) (db)->dbm_flags |= _DBM_DIRDIRTY
37276789Sdim#define dbm_clrdirdirty(db) (db)->dbm_flags &= ~_DBM_DIRDIRTY
38276789Sdim
39276789Sdim
40276789Sdimstatic void	dbm_access(DBM *, long);
41276789Sdimstatic int	getbit(DBM *);
42276789Sdimstatic int	setbit(DBM *);
43276789Sdimstatic int	cmpdatum(datum, datum);
44276789Sdimstatic int	finddatum(char [PBLKSIZ], datum);
45276789Sdimstatic int	delitem(char [PBLKSIZ], int);
46276789Sdimstatic int	additem(char [PBLKSIZ], datum, datum);
47276789Sdimstatic datum	makdatum(char [PBLKSIZ], int);
48276789Sdimstatic long	dcalchash(datum);
49276789Sdim
50276789Sdim/*used to make a dbm file all at once instead of incrementally*/
51276789Sdimvoid
52276789Sdimdbm_setdefwrite(DBM *db)
53276789Sdim{
54276789Sdim	db->dbm_flags |= _DBM_DEFWRITE;
55276789Sdim}
56276789Sdim
57276789Sdimint
58276789Sdimdbm_flush(DBM *db)
59276789Sdim{
60309124Sdim	int ok=0;
61309124Sdim	if (dbm_flushpag(db)<0) ok= -1;
62309124Sdim	if (dbm_flushdir(db)<0) ok= -1;
63	return(ok);
64}
65
66int
67dbm_flushpag(DBM *db)
68{
69	int ok=0;
70	if (dbm_dirty(db)){ /*must page out the page*/
71		(void) lseek(db->dbm_pagf, (long)(db->dbm_pagbno*PBLKSIZ), L_SET);
72		if (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) {
73			db->dbm_flags |= _DBM_IOERR;
74			ok= -1;
75			}
76		dbm_clrdirty(db);
77	}
78	return(ok);
79}
80
81int
82dbm_flushdir(DBM *db)
83{
84	int ok=0;
85	if (dbm_dirdirty(db)){ /*must page out the dir*/
86	(void) lseek(db->dbm_dirf, (long)(db->dbm_dirbno*DBLKSIZ), L_SET);
87	if (write(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ) {
88		ok= -1;
89		}
90	dbm_clrdirdirty(db);
91	}
92	return(ok);
93}
94#define BYTESIZ 8
95#undef setbit
96
97DBM *
98dbm_open(char *file, int flags, int mode)
99{
100	struct stat statb;
101	DBM *db;
102	int	serrno;
103
104	if ((db = (DBM *)malloc(sizeof *db)) == 0) {
105		errno = ENOMEM;
106		return ((DBM *)0);
107	}
108	db->dbm_flags = (flags & 03) == O_RDONLY ? _DBM_RDONLY : 0;
109	if ((flags & 03) == O_WRONLY)
110		flags = (flags & ~03) | O_RDWR;
111	if (strlcpy(db->dbm_pagbuf, file, sizeof (db->dbm_pagbuf)) >=
112	    sizeof (db->dbm_pagbuf) ||
113	    strlcat(db->dbm_pagbuf, ".pag", sizeof (db->dbm_pagbuf)) >=
114	    sizeof (db->dbm_pagbuf)) {
115		/*
116		 * file.pag does not fit into dbm_pagbuf.
117		 * fails with ENAMETOOLONG.
118		 */
119		serrno = ENAMETOOLONG;
120		goto bad;
121	}
122	db->dbm_pagf = open(db->dbm_pagbuf, flags, mode);
123	if (db->dbm_pagf < 0) {
124		serrno = errno;
125		goto bad;
126	}
127	/*
128	 * We know this won't overflow so it is safe to ignore the
129	 * return value; we use strl* to prevent false hits in
130	 * code sweeps.
131	 */
132	(void) strlcpy(db->dbm_pagbuf, file, sizeof (db->dbm_pagbuf));
133	(void) strlcat(db->dbm_pagbuf, ".dir", sizeof (db->dbm_pagbuf));
134	db->dbm_dirf = open(db->dbm_pagbuf, flags, mode);
135	if (db->dbm_dirf < 0) {
136		serrno = errno;
137		goto bad1;
138	}
139	(void) fstat(db->dbm_dirf, &statb);
140	db->dbm_maxbno = statb.st_size*BYTESIZ-1;
141	db->dbm_pagbno = db->dbm_dirbno = -1;
142	return (db);
143bad1:
144	(void) close(db->dbm_pagf);
145bad:
146	free((char *)db);
147	errno = serrno;
148	return ((DBM *)0);
149}
150
151void
152dbm_close(DBM *db)
153{
154	(void) dbm_close_status(db);
155}
156
157/*close with return code*/
158int
159dbm_close_status(DBM *db)
160{
161	int ok;
162	ok=0;
163
164	if (dbm_flush(db) <0)   ok = -1;
165	if (close(db->dbm_dirf)<0) ok= -1;
166	if ( close(db->dbm_pagf)<0) ok= -1;
167	free((char *)db);
168	return (ok);
169}
170
171long
172dbm_forder(DBM *db, datum key)
173{
174	long hash;
175
176	hash = dcalchash(key);
177	for (db->dbm_hmask=0;; db->dbm_hmask=(db->dbm_hmask<<1)+1) {
178		db->dbm_blkno = hash & db->dbm_hmask;
179		db->dbm_bitno = db->dbm_blkno + db->dbm_hmask;
180		if (getbit(db) == 0)
181			break;
182	}
183	return (db->dbm_blkno);
184}
185
186datum
187dbm_fetch(DBM *db, datum key)
188{
189	int i;
190	datum item;
191
192	if (dbm_error(db))
193		goto err;
194	dbm_access(db, dcalchash(key));
195	if ((i = finddatum(db->dbm_pagbuf, key)) >= 0) {
196		item = makdatum(db->dbm_pagbuf, i+1);
197		if (item.dptr != NULL)
198			return (item);
199	}
200err:
201	item.dptr = NULL;
202	item.dsize = 0;
203	return (item);
204}
205
206int
207dbm_delete(DBM *db, datum key)
208{
209	int i;
210
211	if (dbm_error(db))
212		return (-1);
213	if (dbm_rdonly(db)) {
214		errno = EPERM;
215		return (-1);
216	}
217	dbm_access(db, dcalchash(key));
218	if ((i = finddatum(db->dbm_pagbuf, key)) < 0)
219		return (-1);
220	if (!delitem(db->dbm_pagbuf, i))
221		goto err;
222	db->dbm_pagbno = db->dbm_blkno;
223	if (dbm_defwrite(db)) {
224		dbm_setdirty(db);
225	}
226	else {
227	(void) lseek(db->dbm_pagf, (long)(db->dbm_blkno*PBLKSIZ), L_SET);
228	if (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) {
229	err:
230		db->dbm_flags |= _DBM_IOERR;
231		return (-1);
232	}
233	}
234	return (0);
235}
236
237int
238dbm_store(DBM *db, datum key, datum dat, int replace)
239{
240	int i;
241	datum item, item1;
242	char ovfbuf[PBLKSIZ];
243
244	if (dbm_error(db))
245		return (-1);
246	if (dbm_rdonly(db)) {
247		errno = EPERM;
248		return (-1);
249	}
250loop:
251	dbm_access(db, dcalchash(key));
252	if ((i = finddatum(db->dbm_pagbuf, key)) >= 0) {
253		if (!replace)
254			return (1);
255		if (!delitem(db->dbm_pagbuf, i)) {
256			db->dbm_flags |= _DBM_IOERR;
257			return (-1);
258		}
259	}
260	if (!additem(db->dbm_pagbuf, key, dat))
261		goto split;
262	db->dbm_pagbno = db->dbm_blkno;
263	if (dbm_defwrite(db)) {
264		dbm_setdirty(db);
265	}
266	else {
267
268		(void) lseek(db->dbm_pagf, (long)(db->dbm_blkno*PBLKSIZ), L_SET);
269		if (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) {
270			db->dbm_flags |= _DBM_IOERR;
271			return (-1);
272		}
273	}
274	return (0);
275
276split:
277	if (key.dsize+dat.dsize+3*sizeof(short) >= PBLKSIZ) {
278		db->dbm_flags |= _DBM_IOERR;
279		errno = ENOSPC;
280		return (-1);
281	}
282	bzero(ovfbuf, PBLKSIZ);
283	for (i=0;;) {
284		item = makdatum(db->dbm_pagbuf, i);
285		if (item.dptr == NULL)
286			break;
287		if (dcalchash(item) & (db->dbm_hmask+1)) {
288			item1 = makdatum(db->dbm_pagbuf, i+1);
289			if (item1.dptr == NULL) {
290				/*(void) fprintf(stderr, "ndbm: split not paired\n");*/
291				db->dbm_flags |= _DBM_IOERR;
292				break;
293			}
294			if (!additem(ovfbuf, item, item1) ||
295			    !delitem(db->dbm_pagbuf, i)) {
296				db->dbm_flags |= _DBM_IOERR;
297				return (-1);
298			}
299			continue;
300		}
301		i += 2;
302	}
303	db->dbm_pagbno = db->dbm_blkno;
304	(void) lseek(db->dbm_pagf, (long)(db->dbm_blkno*PBLKSIZ), L_SET);
305	if (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) {
306		db->dbm_flags |= _DBM_IOERR;
307		return (-1);
308	}
309	dbm_clrdirty(db); /*clear dirty*/
310	(void) lseek(db->dbm_pagf,
311		(long)((db->dbm_blkno+db->dbm_hmask+1)*PBLKSIZ), L_SET);
312	if (write(db->dbm_pagf, ovfbuf, PBLKSIZ) != PBLKSIZ) {
313		db->dbm_flags |= _DBM_IOERR;
314		return (-1);
315	}
316	if (setbit(db) < 0) {
317		db->dbm_flags |= _DBM_IOERR;
318		return (-1);
319	}
320	goto loop;
321}
322
323static long
324dbm_hashinc(DBM *db, long hash)
325{
326
327	long bit;
328
329	hash &= db->dbm_hmask;
330	bit = db->dbm_hmask+1;
331	for(;;) {
332		bit >>= 1;
333		if(bit == 0)
334			return(0L);
335		if((hash&bit) == 0)
336			return(hash|bit);
337		hash &= ~bit;
338	}
339}
340
341
342
343static datum nullkey= {NULL, 0};
344
345datum
346dbm_firsthash(DBM *db, long hash)
347{
348	int i,j;
349	datum item, bitem;
350
351loop:
352	dbm_access(db, hash);
353	j=0;
354	bitem = makdatum(db->dbm_pagbuf, 0);
355	for(i=2;; i+=2) {
356		item = makdatum(db->dbm_pagbuf, i);
357		if(item.dptr == NULL)
358			break;
359		if(cmpdatum(bitem, item) < 0) {
360			j=i;
361			bitem = item;
362			}
363	}
364	if(bitem.dptr != NULL) {
365	        db->dbm_keyptr = j + 2;
366	        db->dbm_blkptr = db->dbm_blkno;
367		return(bitem);
368	}
369	hash = dbm_hashinc(db,hash);
370	if(hash == 0)
371		return(item); /*null item*/
372	goto loop;
373
374}
375
376datum
377dbm_firstkey(DBM *db)
378{
379
380	db->dbm_blkptr = 0L;
381	db->dbm_keyptr = 0;
382	return (dbm_firsthash(db, 0L));
383}
384
385datum
386dbm_nextkey(DBM *db)
387{
388
389	return (dbm_do_nextkey(db, nullkey));
390}
391
392/*this is used if keyptr-2,blocknum doesn't point to the previous
393specific key allowing the fast hash order search --
394its use indicates user tampering with our state variables,
395which some evil users might do to search from some specific place.
396It finds the first key at or after blkptr,keyptr in block seq order
397this requires looking at all sorts of emtpy blocks in many cases*/
398
399static datum
400dbm_slow_nextkey(DBM *db)
401{
402	struct stat statb;
403	datum item;
404
405	if (dbm_error(db) || fstat(db->dbm_pagf, &statb) < 0)
406		goto err;
407	statb.st_size /= PBLKSIZ;
408
409	for (;;) {
410		if (db->dbm_blkptr != db->dbm_pagbno) {
411
412			if (dbm_dirty(db)) dbm_flushpag(db);
413
414			db->dbm_pagbno = db->dbm_blkptr;
415			(void) lseek(db->dbm_pagf, (long)(db->dbm_blkptr*PBLKSIZ), L_SET);
416			if (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)
417				bzero(db->dbm_pagbuf, PBLKSIZ);
418#ifdef DEBUG
419			else if (chkblk(db->dbm_pagbuf) < 0)
420				db->dbm_flags |= _DBM_IOERR;
421#endif
422		}
423		/*Am I an empty block?*/
424		if (((short *)db->dbm_pagbuf)[0] != 0) {
425			item = makdatum(db->dbm_pagbuf, db->dbm_keyptr);
426			if (item.dptr != NULL) {
427				db->dbm_keyptr += 2;
428				return (item);
429			}
430			db->dbm_keyptr = 0;
431		}
432		/*go to next sequential block*/
433		if (++db->dbm_blkptr >= statb.st_size)
434			break;
435	}
436err:
437	item.dptr = NULL;
438	item.dsize = 0;
439	return (item);
440}
441
442datum
443dbm_do_nextkey(DBM *db, datum inkey)
444{
445	datum item,bitem;
446	long hash;
447	datum key;
448	int f;
449	int i;
450	int j;
451	short *sp;
452	int n;
453	char *p1, *p2;
454
455	if ( dbm_error(db) ) {
456		item.dptr = NULL;
457		item.dsize = 0;
458		return (item);
459	}
460
461	/*user has supplied lastkey*/
462
463	if(inkey.dptr != NULL) {
464	       dbm_access(db, (hash=dcalchash(inkey)));
465	       if ((i = finddatum(db->dbm_pagbuf, inkey)) >= 0) {
466		       db->dbm_keyptr = i + 2;
467		       db->dbm_blkptr = db->dbm_blkno;
468	       }
469	       key=inkey;
470       }
471       else  {
472		/*is this a manual firstkey request? */
473
474		if (db->dbm_blkptr == 0L &&
475			db->dbm_keyptr == 0)
476			return (dbm_firsthash(db, 0L));
477
478		/*no -- get lastkey this is like dbm_access by blkptr*/
479
480		if (db->dbm_blkptr != db->dbm_pagbno) {
481
482			if (dbm_dirty(db)) dbm_flushpag(db);
483			db->dbm_pagbno = db->dbm_blkptr;
484			(void) lseek(db->dbm_pagf, (long)(db->dbm_blkptr*PBLKSIZ), L_SET);
485			if (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)
486				bzero(db->dbm_pagbuf, PBLKSIZ);
487#ifdef DEBUG
488			else if (chkblk(db->dbm_pagbuf) < 0)
489			db->dbm_flags |= _DBM_IOERR;
490#endif
491		}
492		/*now just make up last key datum*/
493		if (db->dbm_keyptr >=2) key= makdatum(db->dbm_pagbuf,(db->dbm_keyptr-2));
494		else key=nullkey;
495
496	/* the keyptr pagbuf have failed us, the user must
497	be an extra clever moron who depends on
498	these variables and their former meaning.
499	If we set the variables this would have got
500	us the key for sure! So give him the old algorithm.*/
501		if (key.dptr == NULL) return (dbm_slow_nextkey(db));
502	}
503
504	/*at this point the last key is paged in and
505	we can proceed as in old dbm -- like Ken did his. */
506
507	f = 1;
508	j=0;
509	sp = (short *)db->dbm_pagbuf;
510
511	for(i=0;; i+=2) {
512
513		/*begin put makdatum inline*/
514
515		if ((unsigned)i >= sp[0]) {
516			item.dptr = NULL;
517			item.dsize = 0;
518			break; /*from below*/
519		}
520		else {
521			if (i > 0) item.dsize = sp[i] - sp[i+1];
522			else item.dsize = PBLKSIZ - sp[i+1];
523			item.dptr = db->dbm_pagbuf+sp[i+1];
524		}
525
526		/*	item = makdatum(db->dbm_pagbuf, i);*/
527		/*end put makdatum inline*/
528
529		if(item.dptr == NULL)
530			break;
531/*inline cmpdatum*/
532
533
534		n = key.dsize;
535		if(n != item.dsize)
536			if( (n - item.dsize) <= 0 ) continue;
537			else { }
538		else {
539			if(n == 0) continue;
540			p1 = key.dptr;
541			p2 = item.dptr;
542			do
543				if(*p1++ != *p2++)
544					if((*--p1 - *--p2) > 0) goto keep_going;
545					else continue;
546			while(--n);
547			continue;
548			}
549
550keep_going:
551
552/*end inline cmpdatum*/
553		/*if(cmpdatum(key, item) <= 0)
554			continue;*/
555		if (f) {
556			bitem = item;
557			j=i;
558			f = 0;
559		}
560		else {
561
562/*		if(cmpdatum(bitem, item) < 0)*/
563
564		n = bitem.dsize;
565		if(n != item.dsize)
566			{
567			if((n - item.dsize) <0) {
568					bitem = item;
569					j=i;
570				}
571			}
572			else  if (n != 0) {
573				p1 = bitem.dptr;
574				p2 = item.dptr;
575				do
576				if(*p1++ != *p2++) {
577					if((*--p1 - *--p2) <0) {
578						bitem = item;
579						j=i;
580					}
581					break;
582				}
583				while(--n);
584			}
585		}
586	}
587
588	if(f == 0) {
589	        db->dbm_keyptr = j + 2;
590	        db->dbm_blkptr = db->dbm_blkno;
591		return (bitem);
592	}
593
594	/*really need hash at this point*/
595	/*if he gave us a key we have already calculated the hash*/
596	/*if not get it*/
597	if (inkey.dptr == NULL) hash=dcalchash(key);
598	hash = dbm_hashinc(db,hash);
599
600	if(hash == 0)
601		return (item); /*null*/
602	/*get first item on next page in hash table order*/
603	return (dbm_firsthash(db, hash));
604
605
606}
607
608static void
609dbm_access(DBM *db, long hash)
610{
611	int b, i, n;
612	long bn;
613	long my_bitno;
614	long my_hmask;
615	long my_blkno;
616
617	for (my_hmask=0;; my_hmask=(my_hmask<<1)+1) {
618		my_blkno = hash & my_hmask;
619		my_bitno = my_blkno + my_hmask;
620		/*getbit inline*/
621		if (my_bitno > db->dbm_maxbno) break;
622		n = my_bitno % BYTESIZ;
623		bn = my_bitno / BYTESIZ;
624		i = bn % DBLKSIZ;
625		b = bn / DBLKSIZ;
626		if (b != db->dbm_dirbno) {
627			if (dbm_dirdirty(db)) dbm_flushdir(db); /*must flush*/
628			db->dbm_dirbno = b;
629			(void) lseek(db->dbm_dirf, (long)(b*DBLKSIZ), L_SET);
630			if (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)
631				bzero(db->dbm_dirbuf, DBLKSIZ);
632		}
633		if ( (db->dbm_dirbuf[i] & (1<<n)) == 0 ) break;
634
635		/*
636		if (getbit(db) == 0)
637			break;
638		*/
639	}
640	/*copy*/
641	db->dbm_blkno=my_blkno;
642	db->dbm_bitno=my_bitno;
643	db->dbm_hmask=my_hmask;
644
645	if (my_blkno != db->dbm_pagbno) {
646		if (dbm_dirty(db)){ /*must page out the page*/
647			(void) lseek(db->dbm_pagf, (long)(db->dbm_pagbno*PBLKSIZ), L_SET);
648			if (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ) {
649				db->dbm_flags |= _DBM_IOERR;
650			}
651		dbm_clrdirty(db);
652		}
653
654		db->dbm_pagbno = my_blkno;
655		(void) lseek(db->dbm_pagf, (long)(my_blkno*PBLKSIZ), L_SET);
656		if (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)
657			bzero(db->dbm_pagbuf, PBLKSIZ);
658#ifdef DEBUG
659		else if (chkblk(db->dbm_pagbuf) < 0)
660			db->dbm_flags |= _DBM_IOERR;
661#endif
662	}
663}
664
665static int
666getbit(DBM *db)
667{
668	long bn;
669	int b, i, n;
670
671
672	if (db->dbm_bitno > db->dbm_maxbno)
673		return (0);
674	n = db->dbm_bitno % BYTESIZ;
675	bn = db->dbm_bitno / BYTESIZ;
676	i = bn % DBLKSIZ;
677	b = bn / DBLKSIZ;
678	if (b != db->dbm_dirbno) {
679		if (dbm_dirdirty(db)) dbm_flushdir(db); /*must flush*/
680		db->dbm_dirbno = b;
681		(void) lseek(db->dbm_dirf, (long)(b*DBLKSIZ), L_SET);
682		if (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)
683			bzero(db->dbm_dirbuf, DBLKSIZ);
684	}
685	return (db->dbm_dirbuf[i] & (1<<n));
686}
687
688static int
689setbit(DBM *db)
690{
691	long bn;
692	int i, n, b;
693
694	if (db->dbm_bitno > db->dbm_maxbno)
695		db->dbm_maxbno = db->dbm_bitno;
696	n = db->dbm_bitno % BYTESIZ;
697	bn = db->dbm_bitno / BYTESIZ;
698	i = bn % DBLKSIZ;
699	b = bn / DBLKSIZ;
700	if (b != db->dbm_dirbno) {
701		if (dbm_dirdirty(db)) dbm_flushdir(db);
702		db->dbm_dirbno = b;
703		(void) lseek(db->dbm_dirf, (long)(b*DBLKSIZ), L_SET);
704		if (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)
705			bzero(db->dbm_dirbuf, DBLKSIZ);
706	}
707	db->dbm_dirbuf[i] |= 1<<n;
708	db->dbm_dirbno = b;
709	if (dbm_defwrite(db)) {
710	dbm_setdirdirty(db);
711	} else{
712	(void) lseek(db->dbm_dirf, (long)(b*DBLKSIZ), L_SET);
713	if (write(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ) {
714		return (-1);
715	}
716	}
717	return (0);
718}
719
720static datum
721makdatum(char buf[PBLKSIZ], int n)
722{
723	short *sp;
724	int t;
725	datum item;
726
727	sp = (short *)buf;
728	if ((unsigned)n >= sp[0]) {
729		item.dptr = NULL;
730		item.dsize = 0;
731		return (item);
732	}
733	t = PBLKSIZ;
734	if (n > 0)
735		t = sp[n];
736	item.dptr = buf+sp[n+1];
737	item.dsize = t - sp[n+1];
738	return (item);
739}
740
741static int
742cmpdatum(datum d1, datum d2)
743{
744	int n;
745	char *p1, *p2;
746
747	n = d1.dsize;
748	if(n != d2.dsize)
749		return(n - d2.dsize);
750	if(n == 0)
751		return(0);
752	p1 = d1.dptr;
753	p2 = d2.dptr;
754	do
755		if(*p1++ != *p2++)
756			return(*--p1 - *--p2);
757	while(--n);
758	return(0);
759}
760
761static int
762finddatum(char buf[PBLKSIZ], datum item)
763{
764	short *sp;
765	int i, n, j;
766
767	sp = (short *)buf;
768	n = PBLKSIZ;
769	for (i=0, j=sp[0]; i<j; i+=2, n = sp[i]) {
770		n -= sp[i+1];
771		if (n != item.dsize)
772			continue;
773		if (n == 0 || bcmp(&buf[sp[i+1]], item.dptr, n) == 0)
774			return (i);
775	}
776	return (-1);
777}
778
779static  int hitab[16]
780 = {    61, 57, 53, 49, 45, 41, 37, 33,
781	29, 25, 21, 17, 13,  9,  5,  1,
782};
783
784static  long hltab[64]
785 = {
786	06100151277L,06106161736L,06452611562L,05001724107L,
787	02614772546L,04120731531L,04665262210L,07347467531L,
788	06735253126L,06042345173L,03072226605L,01464164730L,
789	03247435524L,07652510057L,01546775256L,05714532133L,
790	06173260402L,07517101630L,02431460343L,01743245566L,
791	00261675137L,02433103631L,03421772437L,04447707466L,
792	04435620103L,03757017115L,03641531772L,06767633246L,
793	02673230344L,00260612216L,04133454451L,00615531516L,
794	06137717526L,02574116560L,02304023373L,07061702261L,
795	05153031405L,05322056705L,07401116734L,06552375715L,
796	06165233473L,05311063631L,01212221723L,01052267235L,
797	06000615237L,01075222665L,06330216006L,04402355630L,
798	01451177262L,02000133436L,06025467062L,07121076461L,
799	03123433522L,01010635225L,01716177066L,05161746527L,
800	01736635071L,06243505026L,03637211610L,01756474365L,
801	04723077174L,03642763134L,05750130273L,03655541561L,
802};
803
804static long
805dcalchash(datum item)
806{
807	int s, c, j;
808	char *cp;
809	long hashl;
810	int hashi;
811
812	hashl = 0;
813	hashi = 0;
814	for (cp = item.dptr, s=item.dsize; --s >= 0; ) {
815		c = *cp++;
816		for (j=0; j<BYTESIZ; j+=4) {
817			hashi += hitab[c&017];
818			hashl += hltab[hashi&63];
819			c >>= 4;
820		}
821	}
822	return (hashl);
823}
824
825/*
826 * Delete pairs of items (n & n+1).
827 */
828static int
829delitem(char buf[PBLKSIZ], int n)
830{
831	short *sp, *sp1;
832	int i1, i2;
833
834	sp = (short *)buf;
835	i2 = sp[0];
836	if ((unsigned)n >= i2 || (n & 1))
837		return (0);
838	if (n == i2-2) {
839		sp[0] -= 2;
840		return (1);
841	}
842	i1 = PBLKSIZ;
843	if (n > 0)
844		i1 = sp[n];
845	i1 -= sp[n+2];
846	if (i1 > 0) {
847		i2 = sp[i2];
848		bcopy(&buf[i2], &buf[i2 + i1], sp[n+2] - i2);
849	}
850	sp[0] -= 2;
851	for (sp1 = sp + sp[0], sp += n+1; sp <= sp1; sp++)
852		sp[0] = sp[2] + i1;
853	return (1);
854}
855
856/*
857 * Add pairs of items (item & item1).
858 */
859static int
860additem(char buf[PBLKSIZ], datum item, datum item1)
861{
862	short *sp;
863	int i1, i2;
864
865	sp = (short *)buf;
866	i1 = PBLKSIZ;
867	i2 = sp[0];
868	if (i2 > 0)
869		i1 = sp[i2];
870	i1 -= item.dsize + item1.dsize;
871	if (i1 <= (i2+3) * sizeof(short))
872		return (0);
873	sp[0] += 2;
874	sp[++i2] = i1 + item1.dsize;
875	bcopy(item.dptr, &buf[i1 + item1.dsize], item.dsize);
876	sp[++i2] = i1;
877	bcopy(item1.dptr, &buf[i1], item1.dsize);
878	return (1);
879}
880
881#ifdef DEBUG
882static int
883chkblk(char buf[PBLKSIZ])
884{
885	short *sp;
886	int t, i;
887
888	sp = (short *)buf;
889	t = PBLKSIZ;
890	for (i=0; i<sp[0]; i++) {
891		if (sp[i+1] > t)
892			return (-1);
893		t = sp[i+1];
894	}
895	if (t < (sp[0]+1)*sizeof(short))
896		return (-1);
897	return (0);
898}
899#endif
900