xref: /titanic_50/usr/src/lib/libc/port/gen/ndbm.c (revision 554ff184129088135ad2643c1c9832174a17be88)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 #include "synonyms.h"
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #include <sys/file.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <errno.h>
50 #include <ndbm.h>
51 #include <unistd.h>
52 #include <string.h>
53 
54 /* add support for batched writing for NIS */
55 
56 #define	_DBM_DEFWRITE 0x4
57 #define	_DBM_DIRTY 0x8
58 #define	_DBM_DIRDIRTY 0x10
59 #define	dbm_dirty(db) ((db)->dbm_flags & _DBM_DIRTY)
60 #define	dbm_dirdirty(db) ((db)->dbm_flags & _DBM_DIRDIRTY)
61 #define	dbm_defwrite(db) ((db)->dbm_flags & _DBM_DEFWRITE)
62 #define	dbm_setdirty(db) (db)->dbm_flags |= _DBM_DIRTY
63 #define	dbm_clrdirty(db) (db)->dbm_flags &= ~_DBM_DIRTY
64 #define	dbm_setdirdirty(db) (db)->dbm_flags |= _DBM_DIRDIRTY
65 #define	dbm_clrdirdirty(db) (db)->dbm_flags &= ~_DBM_DIRDIRTY
66 
67 /*
68  * forward declarations
69  */
70 static datum makdatum(char *, int);
71 static unsigned long dcalchash(datum);
72 static void dbm_access(DBM *, unsigned long);
73 static int finddatum(char *, datum);
74 static int delitem(char *, int);
75 static int additem(char *, datum, datum);
76 static int cmpdatum(datum, datum);
77 static int setbit(DBM *);
78 static int getbit(DBM *);
79 static int dbm_flushdir(DBM *);
80 static int dbm_flushpag(DBM *db);
81 
82 /* the following three are required by mapfile-vers */
83 datum dbm_do_nextkey(DBM *, datum);
84 int dbm_close_status(DBM *);
85 
86 /* used to make a dbm file all at once instead of incrementally */
87 void
88 dbm_setdefwrite(DBM *db)
89 {
90 	db->dbm_flags |= _DBM_DEFWRITE;
91 }
92 
93 #undef	dbm_error
94 
95 int
96 dbm_error(DBM *db)
97 {
98 	return (db->dbm_flags & _DBM_IOERR);
99 }
100 
101 #undef	dbm_clearerr
102 
103 int
104 dbm_clearerr(DBM *db)
105 {
106 	return (db->dbm_flags &= ~_DBM_IOERR);
107 }
108 
109 int
110 dbm_flush(DBM *db)
111 {
112 	int ok = 0;
113 	if (dbm_flushpag(db) < 0)
114 		ok = -1;
115 	if (dbm_flushdir(db) < 0)
116 		ok = -1;
117 	return (ok);
118 }
119 
120 static int
121 dbm_flushpag(DBM *db)
122 {
123 	int ok = 0;
124 	off64_t where;
125 
126 	if (dbm_dirty(db)) { /* must page out the page */
127 		where = (((off64_t)db->dbm_pagbno) * PBLKSIZ);
128 		if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
129 		    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)) {
130 			db->dbm_flags |= _DBM_IOERR;
131 			ok = -1;
132 		}
133 		dbm_clrdirty(db);
134 	}
135 	return (ok);
136 }
137 
138 static int
139 dbm_flushdir(DBM *db)
140 {
141 	int ok = 0;
142 	off64_t where;
143 	if (dbm_dirdirty(db)) { /* must page out the dir */
144 		where = (((off64_t)db->dbm_dirbno) * DBLKSIZ);
145 		if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
146 		    (write(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)) {
147 			ok = -1;
148 		}
149 		dbm_clrdirdirty(db);
150 	}
151 	return (ok);
152 }
153 
154 #define	BYTESIZ 8
155 #undef setbit
156 
157 DBM *
158 dbm_open(const char *file, int flags, mode_t mode)
159 {
160 	struct stat64 statb;
161 	DBM *db;
162 	int	serrno;
163 
164 	if ((db = (DBM *)malloc(sizeof (*db))) == 0) {
165 		errno = ENOMEM;
166 		return ((DBM *)0);
167 	}
168 	db->dbm_flags = (flags & 03) == O_RDONLY ? _DBM_RDONLY : 0;
169 	if ((flags & 03) == O_WRONLY)
170 		flags = (flags & ~03) | O_RDWR;
171 	if (strlcpy(db->dbm_pagbuf, file, sizeof (db->dbm_pagbuf)) >=
172 	    sizeof (db->dbm_pagbuf) ||
173 	    strlcat(db->dbm_pagbuf, ".pag", sizeof (db->dbm_pagbuf)) >=
174 	    sizeof (db->dbm_pagbuf)) {
175 		/*
176 		 * file.pag does not fit into dbm_pagbuf.
177 		 * fail with ENAMETOOLONG.
178 		 */
179 		serrno = ENAMETOOLONG;
180 		goto bad;
181 	}
182 	db->dbm_pagf = open64(db->dbm_pagbuf, flags, mode);
183 	if (db->dbm_pagf < 0) {
184 		serrno = errno;
185 		goto bad;
186 	}
187 	/*
188 	 * We know this won't overflow so it is safe to ignore the
189 	 * return value; we use strl* to prevent false hits in
190 	 * code sweeps.
191 	 */
192 	(void) strlcpy(db->dbm_pagbuf, file, sizeof (db->dbm_pagbuf));
193 	(void) strlcat(db->dbm_pagbuf, ".dir", sizeof (db->dbm_pagbuf));
194 	db->dbm_dirf = open64(db->dbm_pagbuf, flags, mode);
195 	if (db->dbm_dirf < 0) {
196 		serrno = errno;
197 		goto bad1;
198 	}
199 	(void) fstat64(db->dbm_dirf, &statb);
200 	db->dbm_maxbno = statb.st_size * BYTESIZ-1;
201 	db->dbm_pagbno = db->dbm_dirbno = -1;
202 	return (db);
203 bad1:
204 	(void) close(db->dbm_pagf);
205 bad:
206 	free((char *)db);
207 	errno = serrno;
208 	return ((DBM *)0);
209 }
210 
211 void
212 dbm_close(DBM *db)
213 {
214 (void) dbm_close_status(db);
215 }
216 
217 /* close with return code */
218 int
219 dbm_close_status(DBM *db)
220 {
221 	int ok;
222 	ok = 0;
223 
224 	if (dbm_flush(db) < 0)
225 		ok = -1;
226 	if (close(db->dbm_dirf) < 0)
227 		ok = -1;
228 	if (close(db->dbm_pagf) < 0)
229 		ok = -1;
230 	free((char *)db);
231 	return (ok);
232 }
233 
234 long
235 dbm_forder(DBM *db, datum key)
236 {
237 	unsigned long hash;
238 
239 	hash = dcalchash(key);
240 	for (db->dbm_hmask = 0; ; db->dbm_hmask = (db->dbm_hmask<<1)+1) {
241 		db->dbm_blkno = hash & db->dbm_hmask;
242 		db->dbm_bitno = db->dbm_blkno + db->dbm_hmask;
243 		if (getbit(db) == 0)
244 			break;
245 	}
246 	return (db->dbm_blkno);
247 }
248 
249 datum
250 dbm_fetch(DBM *db, datum key)
251 {
252 	int i;
253 	datum item;
254 
255 	if (dbm_error(db))
256 		goto err;
257 	dbm_access(db, dcalchash(key));
258 	if ((i = finddatum(db->dbm_pagbuf, key)) >= 0) {
259 		item = makdatum(db->dbm_pagbuf, i+1);
260 		if (item.dptr != NULL)
261 			return (item);
262 	}
263 err:
264 	item.dptr = NULL;
265 	item.dsize = 0;
266 	return (item);
267 }
268 
269 int
270 dbm_delete(DBM *db, datum key)
271 {
272 	int i;
273 	off64_t where;
274 
275 	if (dbm_error(db))
276 		return (-1);
277 	if (dbm_rdonly(db)) {
278 		errno = EPERM;
279 		return (-1);
280 	}
281 	dbm_access(db, dcalchash(key));
282 	if ((i = finddatum(db->dbm_pagbuf, key)) < 0)
283 		return (-1);
284 	if (!delitem(db->dbm_pagbuf, i))
285 		goto err;
286 	db->dbm_pagbno = db->dbm_blkno;
287 	if (dbm_defwrite(db)) {
288 		dbm_setdirty(db);
289 	} else {
290 		where = (((off64_t)db->dbm_blkno) * PBLKSIZ);
291 		if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
292 		    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)) {
293 			err:
294 				db->dbm_flags |= _DBM_IOERR;
295 				return (-1);
296 		}
297 	}
298 	return (0);
299 }
300 
301 int
302 dbm_store(DBM *db, datum key, datum dat, int replace)
303 {
304 	int i;
305 	datum item, item1;
306 	char ovfbuf[PBLKSIZ];
307 	unsigned long hashdiff, key_hash, item_hash;
308 	off64_t where;
309 
310 	if (dbm_error(db))
311 		return (-1);
312 	if (dbm_rdonly(db)) {
313 		errno = EPERM;
314 		return (-1);
315 	}
316 loop:
317 	key_hash = dcalchash(key);
318 	dbm_access(db, key_hash);
319 	if ((i = finddatum(db->dbm_pagbuf, key)) >= 0) {
320 		if (!replace)
321 			return (1);
322 		if (!delitem(db->dbm_pagbuf, i)) {
323 			db->dbm_flags |= _DBM_IOERR;
324 			return (-1);
325 		}
326 	}
327 	if (!additem(db->dbm_pagbuf, key, dat))
328 		goto split;
329 	db->dbm_pagbno = db->dbm_blkno;
330 	if (dbm_defwrite(db)) {
331 		dbm_setdirty(db);
332 	} else {
333 		where = (((off64_t)db->dbm_blkno) * PBLKSIZ);
334 		if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
335 		    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)) {
336 			db->dbm_flags |= _DBM_IOERR;
337 			return (-1);
338 		}
339 	}
340 	return (0);
341 split:
342 	hashdiff = 0;
343 	if (key.dsize + dat.dsize + 3 * (int)sizeof (short) >= PBLKSIZ) {
344 		db->dbm_flags |= _DBM_IOERR;
345 		errno = ENOSPC;
346 		return (-1);
347 	}
348 	(void) memset(ovfbuf, 0, PBLKSIZ);
349 	for (i = 0; ; ) {
350 		item = makdatum(db->dbm_pagbuf, i);
351 		if (item.dptr == NULL)
352 			break;
353 		item_hash = dcalchash(item);
354 		if (item_hash != key_hash) {
355 			hashdiff++;
356 		}
357 
358 		if (item_hash & (db->dbm_hmask+1)) {
359 			item1 = makdatum(db->dbm_pagbuf, i+1);
360 			if (item1.dptr == NULL) {
361 				/* (void) fprintf(stderr, "ndbm: */
362 				/* split not paired\n"); */
363 				db->dbm_flags |= _DBM_IOERR;
364 				break;
365 			}
366 			if (!additem(ovfbuf, item, item1) ||
367 			    !delitem(db->dbm_pagbuf, i)) {
368 				db->dbm_flags |= _DBM_IOERR;
369 				return (-1);
370 			}
371 			continue;
372 		}
373 		i += 2;
374 	}
375 	db->dbm_pagbno = db->dbm_blkno;
376 	where = (((off64_t)db->dbm_blkno) * PBLKSIZ);
377 	if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
378 	    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)) {
379 		db->dbm_flags |= _DBM_IOERR;
380 		return (-1);
381 	}
382 	dbm_clrdirty(db); /* clear dirty */
383 	where = (((off64_t)db->dbm_blkno + db->dbm_hmask + 1) * PBLKSIZ);
384 	if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
385 	    (write(db->dbm_pagf, ovfbuf, PBLKSIZ) != PBLKSIZ)) {
386 		db->dbm_flags |= _DBM_IOERR;
387 		return (-1);
388 	}
389 	if (setbit(db) < 0) {
390 		db->dbm_flags |= _DBM_IOERR;
391 		return (-1);
392 	}
393 	if (hashdiff == 0) {
394 		db->dbm_flags |= _DBM_IOERR;
395 		return (-1);
396 	}
397 	goto loop;
398 }
399 
400 static unsigned long
401 dbm_hashinc(DBM *db, unsigned long hash)
402 {
403 	long bit;
404 
405 	hash &= db->dbm_hmask;
406 	bit = db->dbm_hmask+1;
407 	for (;;) {
408 		bit >>= 1;
409 		if (bit == 0)
410 			return (0L);
411 		if ((hash&bit) == 0)
412 			return (hash|bit);
413 		hash &= ~bit;
414 	}
415 }
416 
417 
418 
419 static datum nullkey = {NULL, 0};
420 
421 datum
422 dbm_firsthash(DBM *db, unsigned long hash)
423 {
424 	int i, j;
425 	datum item, bitem;
426 
427 loop:
428 	dbm_access(db, hash);
429 	j = 0;
430 	bitem = makdatum(db->dbm_pagbuf, 0);
431 	for (i = 2; ; i += 2) {
432 		item = makdatum(db->dbm_pagbuf, i);
433 		if (item.dptr == NULL)
434 			break;
435 		if (cmpdatum(bitem, item) < 0) {
436 			j = i;
437 			bitem = item;
438 		}
439 	}
440 	if (bitem.dptr != NULL) {
441 		db->dbm_keyptr = j + 2;
442 		db->dbm_blkptr = db->dbm_blkno;
443 		return (bitem);
444 	}
445 	hash = dbm_hashinc(db, hash);
446 	if (hash == 0)
447 		return (item); /* null item */
448 	goto loop;
449 
450 }
451 
452 datum
453 dbm_firstkey(DBM *db)
454 {
455 
456 	db->dbm_blkptr = 0L;
457 	db->dbm_keyptr = 0;
458 	return (dbm_firsthash(db, 0L));
459 }
460 
461 datum
462 dbm_nextkey(DBM *db)
463 {
464 
465 	return (dbm_do_nextkey(db, nullkey));
466 }
467 
468 /*
469  * this is used if keyptr-2, blocknum doesn't point to the previous
470  * specific key allowing the fast hash order search --
471  * its use indicates user tampering with our state variables,
472  * which some evil users might do to search from some specific place.
473  * It finds the first key at or after blkptr, keyptr in block seq order
474  * this requires looking at all sorts of emtpy blocks in many cases
475  */
476 
477 static
478 datum
479 dbm_slow_nextkey(DBM *db)
480 {
481 
482 	struct stat64 statb;
483 	datum item;
484 	off64_t where;
485 
486 	if (dbm_error(db) || fstat64(db->dbm_pagf, &statb) < 0)
487 		goto err;
488 	statb.st_size /= PBLKSIZ;
489 
490 	for (;;) {
491 		if (db->dbm_blkptr != db->dbm_pagbno) {
492 
493 			if (dbm_dirty(db)) (void) dbm_flushpag(db);
494 
495 			db->dbm_pagbno = db->dbm_blkptr;
496 			where = (((off64_t)db->dbm_blkptr) * PBLKSIZ);
497 			if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
498 			    (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) !=
499 				PBLKSIZ))
500 				(void) memset(db->dbm_pagbuf, 0, PBLKSIZ);
501 #ifdef DEBUG
502 			else if (chkblk(db->dbm_pagbuf) < 0)
503 				db->dbm_flags |= _DBM_IOERR;
504 #endif
505 		}
506 		/* Am I an empty block? */
507 		if (((short *)(uintptr_t)db->dbm_pagbuf)[0] != 0) {
508 			item = makdatum(db->dbm_pagbuf, db->dbm_keyptr);
509 			if (item.dptr != NULL) {
510 				db->dbm_keyptr += 2;
511 				return (item);
512 			}
513 			db->dbm_keyptr = 0;
514 		}
515 		/* go to next sequential block */
516 		if (++db->dbm_blkptr >= statb.st_size)
517 			break;
518 	}
519 err:
520 	item.dptr = NULL;
521 	item.dsize = 0;
522 	return (item);
523 }
524 
525 
526 
527 datum
528 dbm_do_nextkey(DBM *db, datum inkey)
529 {
530 	datum item, bitem;
531 	unsigned long hash;
532 	datum key;
533 	int f;
534 	int i;
535 	int j;
536 	short *sp;
537 	long n;
538 	char *p1, *p2;
539 	off64_t where;
540 
541 	if (dbm_error(db)) {
542 		item.dptr = NULL;
543 		item.dsize = 0;
544 		return (item);
545 	}
546 
547 	/* user has supplied lastkey */
548 
549 	if (inkey.dptr != NULL) {
550 		dbm_access(db, (hash = dcalchash(inkey)));
551 		if ((i = finddatum(db->dbm_pagbuf, inkey)) >= 0) {
552 			db->dbm_keyptr = i + 2;
553 			db->dbm_blkptr = db->dbm_blkno;
554 		}
555 		key = inkey;
556 	} else  {
557 		/* is this a manual firstkey request? */
558 
559 		if (db->dbm_blkptr == 0L &&
560 			db->dbm_keyptr == 0)
561 			return (dbm_firsthash(db, 0L));
562 
563 		/* no -- get lastkey this is like dbm_access by blkptr */
564 
565 		if (db->dbm_blkptr != db->dbm_pagbno) {
566 
567 			if (dbm_dirty(db)) (void) dbm_flushpag(db);
568 			db->dbm_pagbno = db->dbm_blkptr;
569 			where = (((off64_t)db->dbm_blkptr) * PBLKSIZ);
570 			if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
571 			    (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) !=
572 				PBLKSIZ))
573 				(void) memset(db->dbm_pagbuf, 0, PBLKSIZ);
574 #ifdef DEBUG
575 			else if (chkblk(db->dbm_pagbuf) < 0)
576 			db->dbm_flags |= _DBM_IOERR;
577 #endif
578 		}
579 		/* now just make up last key datum */
580 		if (db->dbm_keyptr >= 2)
581 			key = makdatum(db->dbm_pagbuf, (db->dbm_keyptr-2));
582 		else key = nullkey;
583 
584 		/*
585 		 * the keyptr pagbuf have failed us, the user must
586 		 * be an extra clever moron who depends on
587 		 * these variables and their former meaning.
588 		 * If we set the variables this would have got
589 		 * us the key for sure! So give him the old algorithm.
590 		 */
591 		if (key.dptr == NULL)
592 			return (dbm_slow_nextkey(db));
593 	}
594 
595 	/*
596 	 * at this point the last key is paged in and
597 	 * we can proceed as in old dbm -- like Ken did his.
598 	 */
599 
600 	f = 1;
601 	j = 0;
602 	sp = (short *)(uintptr_t)db->dbm_pagbuf;
603 
604 	for (i = 0; ; i += 2) {
605 
606 		/* begin put makdatum inline */
607 
608 		if ((unsigned)i >= sp[0]) {
609 			item.dptr = NULL;
610 			item.dsize = 0;
611 			break; /* from below */
612 		} else {
613 			if (i > 0) item.dsize = sp[i] - sp[i + 1];
614 			else item.dsize = PBLKSIZ - sp[i + 1];
615 			item.dptr = db->dbm_pagbuf+sp[i + 1];
616 		}
617 
618 		/* item = makdatum(db->dbm_pagbuf, i); */
619 		/* end put makdatum inline */
620 
621 		if (item.dptr == NULL)
622 			break;
623 /* inline cmpdatum */
624 
625 
626 		n = key.dsize;
627 		if (n != item.dsize) {
628 			if ((n - item.dsize) <= 0)
629 				continue;
630 		} else {
631 			if (n == 0) continue;
632 			p1 = key.dptr;
633 			p2 = item.dptr;
634 			do {
635 				if (*p1++ != *p2++)
636 					if ((*--p1 - *--p2) > 0)
637 						goto keep_going;
638 					else continue;
639 			} while (--n);
640 			continue;
641 		}
642 
643 keep_going:
644 
645 /* end inline cmpdatum */
646 		/*
647 		 * if (cmpdatum(key, item) <= 0)
648 		 *	continue;
649 		 */
650 
651 		if (f) {
652 			bitem = item;
653 			j = i;
654 			f = 0;
655 		} else {
656 
657 			/* if (cmpdatum(bitem, item) < 0) */
658 
659 			n = bitem.dsize;
660 			if (n != item.dsize) {
661 				if ((n - item.dsize) < 0) {
662 					bitem = item;
663 					j = i;
664 				}
665 			} else
666 				if (n != 0) {
667 					p1 = bitem.dptr;
668 					p2 = item.dptr;
669 					do {
670 					if (*p1++ != *p2++) {
671 						if ((*--p1 - *--p2) < 0) {
672 							bitem = item;
673 							j = i;
674 						}
675 						break;
676 					}
677 					} while (--n);
678 				}
679 			}
680 	}
681 
682 	if (f == 0) {
683 		db->dbm_keyptr = j + 2;
684 		db->dbm_blkptr = db->dbm_blkno;
685 		return (bitem);
686 	}
687 
688 	/* really need hash at this point */
689 	/* if he gave us a key we have already calculated the hash */
690 	/* if not get it */
691 	if (inkey.dptr == NULL)
692 		hash = dcalchash(key);
693 	hash = dbm_hashinc(db, hash);
694 
695 	if (hash == 0)
696 		return (item); /* null */
697 	/* get first item on next page in hash table order */
698 	return (dbm_firsthash(db, hash));
699 
700 
701 }
702 
703 static void
704 dbm_access(DBM *db, unsigned long hash)
705 {
706 	long b, i, n;
707 	long bn;
708 	long my_bitno;
709 	long my_hmask;
710 	long my_blkno;
711 	int j = (sizeof (my_hmask)) * 8;
712 	off64_t where;
713 
714 	for (my_hmask = 0; j-- > 0; my_hmask = (my_hmask<<1) + 1) {
715 		my_blkno = hash & my_hmask;
716 		my_bitno = my_blkno + my_hmask;
717 		/* getbit inline */
718 		if (my_bitno > db->dbm_maxbno) break;
719 		n = my_bitno % BYTESIZ;
720 		bn = my_bitno / BYTESIZ;
721 		i = bn % DBLKSIZ;
722 		b = bn / DBLKSIZ;
723 		if (b != db->dbm_dirbno) {
724 			if (dbm_dirdirty(db))
725 				(void) dbm_flushdir(db); /* must flush */
726 			db->dbm_dirbno = b;
727 			where = (((off64_t)b) * DBLKSIZ);
728 			if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
729 			    (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) !=
730 				DBLKSIZ))
731 				(void) memset(db->dbm_dirbuf, 0, DBLKSIZ);
732 		}
733 		if ((db->dbm_dirbuf[i] & (1<<n)) == 0) break;
734 
735 		/*
736 		 * if (getbit(db) == 0)
737 		 *	break;
738 		 */
739 	}
740 	/* copy */
741 	db->dbm_blkno = my_blkno;
742 	db->dbm_bitno = my_bitno;
743 	db->dbm_hmask = my_hmask;
744 
745 	if (my_blkno != db->dbm_pagbno) {
746 		if (dbm_dirty(db)) { /* must page out the page */
747 			where = (((off64_t)db->dbm_pagbno) * PBLKSIZ);
748 			if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
749 			    (write(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) !=
750 				PBLKSIZ)) {
751 				db->dbm_flags |= _DBM_IOERR;
752 			}
753 		dbm_clrdirty(db);
754 		}
755 
756 		db->dbm_pagbno = my_blkno;
757 		where = (((off64_t)my_blkno) * PBLKSIZ);
758 		if ((lseek64(db->dbm_pagf, where, L_SET) != where) ||
759 		    (read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ))
760 			(void) memset(db->dbm_pagbuf, 0, PBLKSIZ);
761 #ifdef DEBUG
762 		else if (chkblk(db->dbm_pagbuf) < 0)
763 			db->dbm_flags |= _DBM_IOERR;
764 #endif
765 	}
766 }
767 
768 static int
769 getbit(DBM *db)
770 {
771 	long bn;
772 	long b, i, n;
773 	off64_t where;
774 
775 	if (db->dbm_bitno > db->dbm_maxbno)
776 		return (0);
777 	n = db->dbm_bitno % BYTESIZ;
778 	bn = db->dbm_bitno / BYTESIZ;
779 	i = bn % DBLKSIZ;
780 	b = bn / DBLKSIZ;
781 	if (b != db->dbm_dirbno) {
782 		if (dbm_dirdirty(db))
783 			(void) dbm_flushdir(db); /* must flush */
784 		db->dbm_dirbno = b;
785 		where = (((off64_t)b) * DBLKSIZ);
786 		if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
787 		    (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ))
788 			(void) memset(db->dbm_dirbuf, 0, DBLKSIZ);
789 	}
790 	return (db->dbm_dirbuf[i] & (1<<n));
791 }
792 
793 static int
794 setbit(DBM *db)
795 {
796 	long bn;
797 	long i, n, b;
798 	off64_t where;
799 
800 	if (db->dbm_bitno > db->dbm_maxbno)
801 		db->dbm_maxbno = db->dbm_bitno;
802 	n = db->dbm_bitno % BYTESIZ;
803 	bn = db->dbm_bitno / BYTESIZ;
804 	i = bn % DBLKSIZ;
805 	b = bn / DBLKSIZ;
806 	if (b != db->dbm_dirbno) {
807 		if (dbm_dirdirty(db))
808 			(void) dbm_flushdir(db);
809 		db->dbm_dirbno = b;
810 		where = (((off64_t)b) * DBLKSIZ);
811 		if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
812 		    (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ))
813 			(void) memset(db->dbm_dirbuf, 0, DBLKSIZ);
814 	}
815 	db->dbm_dirbuf[i] |= 1<<n;
816 	db->dbm_dirbno = b;
817 	if (dbm_defwrite(db)) {
818 		dbm_setdirdirty(db);
819 	} else {
820 		where = (((off64_t)b) * DBLKSIZ);
821 		if ((lseek64(db->dbm_dirf, where, L_SET) != where) ||
822 		    (write(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)) {
823 			return (-1);
824 		}
825 	}
826 	return (0);
827 }
828 
829 static datum
830 makdatum(char buf[PBLKSIZ], int n)
831 {
832 	short *sp;
833 	int t;
834 	datum item;
835 
836 	sp = (short *)(uintptr_t)buf;
837 	if ((unsigned)n >= sp[0]) {
838 		item.dptr = NULL;
839 		item.dsize = 0;
840 		return (item);
841 	}
842 	t = PBLKSIZ;
843 	if (n > 0)
844 		t = sp[n];
845 	item.dptr = buf + sp[n + 1];
846 	item.dsize = t - sp[n + 1];
847 	return (item);
848 }
849 
850 static int
851 cmpdatum(datum d1, datum d2)
852 {
853 	long n;
854 	char *p1, *p2;
855 
856 	n = d1.dsize;
857 	if (n != d2.dsize)
858 		return ((int)(n - d2.dsize));
859 	if (n == 0)
860 		return (0);
861 	p1 = d1.dptr;
862 	p2 = d2.dptr;
863 	do {
864 		if (*p1 != *p2)
865 			return (*p1 - *p2);
866 		else {
867 			p1++;
868 			p2++;
869 		}
870 	} while (--n);
871 	return (0);
872 }
873 
874 static int
875 finddatum(char buf[PBLKSIZ], datum item)
876 {
877 	short *sp;
878 	int i, n, j;
879 
880 	sp = (short *)(uintptr_t)buf;
881 	n = PBLKSIZ;
882 	for (i = 0, j = sp[0]; i < j; i += 2, n = sp[i]) {
883 		n -= sp[i + 1];
884 		if (n != item.dsize)
885 			continue;
886 		if (n == 0 || memcmp(&buf[sp[i+1]], item.dptr, n) == 0)
887 			return (i);
888 	}
889 	return (-1);
890 }
891 
892 static const int hitab[16]
893 /*
894  * ken's
895  * {
896  *	055, 043, 036, 054, 063, 014, 004, 005,
897  *	010, 064, 077, 000, 035, 027, 025, 071,
898  * };
899  */
900 	= {    61, 57, 53, 49, 45, 41, 37, 33,
901 		29, 25, 21, 17, 13,  9,  5,  1,
902 };
903 
904 /* could consider to make it 32-bit int table to minimize memory usage */
905 static const long hltab[64]
906 	= {
907 	06100151277L, 06106161736L, 06452611562L, 05001724107L,
908 	02614772546L, 04120731531L, 04665262210L, 07347467531L,
909 	06735253126L, 06042345173L, 03072226605L, 01464164730L,
910 	03247435524L, 07652510057L, 01546775256L, 05714532133L,
911 	06173260402L, 07517101630L, 02431460343L, 01743245566L,
912 	00261675137L, 02433103631L, 03421772437L, 04447707466L,
913 	04435620103L, 03757017115L, 03641531772L, 06767633246L,
914 	02673230344L, 00260612216L, 04133454451L, 00615531516L,
915 	06137717526L, 02574116560L, 02304023373L, 07061702261L,
916 	05153031405L, 05322056705L, 07401116734L, 06552375715L,
917 	06165233473L, 05311063631L, 01212221723L, 01052267235L,
918 	06000615237L, 01075222665L, 06330216006L, 04402355630L,
919 	01451177262L, 02000133436L, 06025467062L, 07121076461L,
920 	03123433522L, 01010635225L, 01716177066L, 05161746527L,
921 	01736635071L, 06243505026L, 03637211610L, 01756474365L,
922 	04723077174L, 03642763134L, 05750130273L, 03655541561L,
923 };
924 
925 static unsigned long
926 dcalchash(datum item)
927 {
928 	long s;
929 	int c, j;
930 	char *cp;
931 	unsigned long hashl;
932 	long hashi;
933 
934 	hashl = 0;
935 	hashi = 0;
936 	for (cp = item.dptr, s = item.dsize; --s >= 0; ) {
937 		c = *cp++;
938 		for (j = 0; j < BYTESIZ; j += 4) {
939 			hashi += hitab[c&017];
940 			hashl += hltab[hashi&63];
941 			c >>= 4;
942 		}
943 	}
944 	return (hashl);
945 }
946 
947 /*
948  * Delete pairs of items (n & n+1).
949  */
950 static int
951 delitem(char buf[PBLKSIZ], int n)
952 {
953 	short *sp, *sp1;
954 	int i1, i2;
955 
956 	sp = (short *)(uintptr_t)buf;
957 	i2 = sp[0];
958 	if ((unsigned)n >= i2 || (n & 1))
959 		return (0);
960 	if (n == i2-2) {
961 		sp[0] -= 2;
962 		return (1);
963 	}
964 	i1 = PBLKSIZ;
965 	if (n > 0)
966 		i1 = sp[n];
967 	i1 -= sp[n+2];
968 	if (i1 > 0) {
969 		i2 = sp[i2];
970 		(void) memmove(&buf[i2 + i1], &buf[i2], sp[n+2] - i2);
971 	}
972 	sp[0] -= 2;
973 	for (sp1 = sp + sp[0], sp += n+1; sp <= sp1; sp++)
974 		sp[0] = sp[2] + i1;
975 	return (1);
976 }
977 
978 /*
979  * Add pairs of items (item & item1).
980  */
981 static int
982 additem(char buf[PBLKSIZ], datum item, datum item1)
983 {
984 	short *sp;
985 	int i1, i2;
986 
987 	sp = (short *)(uintptr_t)buf;
988 	i1 = PBLKSIZ;
989 	i2 = sp[0];
990 	if (i2 > 0)
991 		i1 = sp[i2];
992 	i1 -= item.dsize + item1.dsize;
993 	if (i1 <= (i2+3) * (int)sizeof (short))
994 		return (0);
995 	sp[0] += 2;
996 	sp[++i2] = (short)(i1 + item1.dsize);
997 	(void) memmove(&buf[i1 + item1.dsize], item.dptr, item.dsize);
998 	sp[++i2] = i1;
999 	(void) memmove(&buf[i1], item1.dptr, item1.dsize);
1000 	return (1);
1001 }
1002 
1003 #ifdef DEBUG
1004 static int
1005 chkblk(char buf[PBLKSIZ])
1006 {
1007 	short *sp;
1008 	int t, i;
1009 
1010 	sp = (short *)buf;
1011 	t = PBLKSIZ;
1012 	for (i = 0; i < sp[0]; i++) {
1013 		if (sp[i + 1] > t)
1014 			return (-1);
1015 		t = sp[i + 1];
1016 	}
1017 	if (t < (sp[0] + 1) * sizeof (short))
1018 		return (-1);
1019 	return (0);
1020 }
1021 #endif
1022