home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2386 < prev    next >
Encoding:
Internet Message Format  |  1990-12-28  |  24.8 KB

  1. From: oz@nexus.YorkU.CA (Ozan Yigit)
  2. Newsgroups: alt.sources
  3. Subject: sdbm part 2
  4. Message-ID: <19120@yunexus.YorkU.CA>
  5. Date: 15 Dec 90 15:38:34 GMT
  6.  
  7. #! /bin/sh
  8. # This is a shell archive.  Remove anything before this line, then unpack
  9. # it by saving it into a file and typing "sh file".  To overwrite existing
  10. # files, type "sh file -c".  You can also feed this as standard input via
  11. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  12. # will see the following message at the end:
  13. #        "End of archive 2 (of 2)."
  14. # Contents:  dist/readme.ms dist/sdbm.c
  15. # Wrapped by oz@yunexus on Sat Dec 15 10:24:20 1990
  16. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  17. if test -f 'dist/readme.ms' -a "${1}" != "-c" ; then 
  18.   echo shar: Will not clobber existing file \"'dist/readme.ms'\"
  19. else
  20. echo shar: Extracting \"'dist/readme.ms'\" \(11691 characters\)
  21. sed "s/^X//" >'dist/readme.ms' <<'END_OF_FILE'
  22. X.\" tbl | readme.ms | [tn]roff -ms | ...
  23. X.\" note the "C" (courier) and "CB" fonts: you will probably have to
  24. X.\" change these.
  25. X.\" $Id: readme.ms,v 1.1 90/12/13 13:09:15 oz Exp Locker: oz $
  26. X
  27. X.de P1
  28. X.br
  29. X.nr dT 4
  30. X.nf
  31. X.ft C
  32. X.sp .5
  33. X.nr t \\n(dT*\\w'x'u
  34. X.ta 1u*\\ntu 2u*\\ntu 3u*\\ntu 4u*\\ntu 5u*\\ntu 6u*\\ntu 7u*\\ntu 8u*\\ntu 9u*\\ntu 10u*\\ntu 11u*\\ntu 12u*\\ntu 13u*\\ntu 14u*\\ntu
  35. X..
  36. X.de P2
  37. X.br
  38. X.ft 1
  39. X.br
  40. X.sp .5
  41. X.br
  42. X.fi
  43. X..
  44. X.\" CW uses the typewriter/courier font.
  45. X.de CW
  46. X\fC\\$1\\fP\\$2
  47. X..
  48. X
  49. X.\" Footnote numbering [by Henry Spencer]
  50. X.\" <text>\*f for a footnote number..
  51. X.\" .FS
  52. X.\" \*F <footnote text>
  53. X.\" .FE
  54. X.\"
  55. X.ds f \\u\\s-2\\n+f\\s+2\\d
  56. X.nr f 0 1
  57. X.ds F \\n+F.
  58. X.nr F 0 1
  59. X
  60. X.ND
  61. X.LP
  62. X.TL
  63. X\fIsdbm\fP \(em Substitute DBM
  64. X.br
  65. Xor
  66. X.br
  67. XBerkeley \fIndbm\fP for Every UN*X\** Made Simple
  68. X.AU
  69. XOzan (oz) Yigit
  70. X.AI
  71. XThe Guild of PD Software Toolmakers
  72. XToronto - Canada
  73. X.sp
  74. Xoz@nexus.yorku.ca
  75. X.LP
  76. X.FS
  77. XUN*X is not a trademark of any (dis)organization.
  78. X.FE
  79. X.sp 2
  80. X\fIImplementation is the sincerest form of flattery. \(em L. Peter Deutsch\fP
  81. X.SH
  82. XA The Clone of the \fIndbm\fP library
  83. X.PP
  84. XThe sources accompanying this notice \(em \fIsdbm\fP \(em constitute
  85. Xthe first public release (Dec. 1990) of a complete clone of
  86. Xthe Berkeley UN*X \fIndbm\fP library. The \fIsdbm\fP library is meant to
  87. Xclone the proven functionality of \fIndbm\fP as closely as possible,
  88. Xincluding a few improvements. It is practical, easy to understand, and
  89. Xcompatible.
  90. XThe \fIsdbm\fP library is not derived from any licensed, proprietary or
  91. Xcopyrighted software.
  92. X.PP
  93. XThe \fIsdbm\fP implementation is based on a 1978 algorithm
  94. X[Lar78] by P.-A. (Paul) Larson known as ``Dynamic Hashing''.
  95. XIn the course of searching for a substitute for \fIndbm\fP, I
  96. Xprototyped three different external-hashing algorithms [Lar78, Fag79, Lit80]
  97. Xand ultimately chose Larson's algorithm as a basis of the \fIsdbm\fP
  98. Ximplementation. The Bell Labs
  99. X\fIdbm\fP (and therefore \fIndbm\fP) is based on an algorithm invented by
  100. XKen Thompson, [Tho90, Tor87] and predates Larson's work.
  101. X.PP
  102. XThe \fIsdbm\fR programming interface is totally compatible
  103. Xwith \fIndbm\fP and includes a slight improvement in database initialization.
  104. XIt is also expected to be binary-compatible under most UN*X versions that
  105. Xsupport the \fIndbm\fP library.
  106. X.PP
  107. XThe \fIsdbm\fP implementation shares the shortcomings of the \fIndbm\fP
  108. Xlibrary, as a side effect of various simplifications to the original Larson
  109. Xalgorithm. It does produce \fIholes\fP in the page file as it writes
  110. Xpages past the end of file. (Larson's paper include a clever solution to
  111. Xthis problem that is a result of using the hash value directly as a block
  112. Xaddress.) On the other hand, extensive tests seem to indicate that \fIsdbm\fP
  113. Xcreates fewer holes in general, and the resulting pagefiles are
  114. Xsmaller. The \fIsdbm\fP implementation is also faster than \fIndbm\fP
  115. Xin database creation.
  116. XUnlike the \fIndbm\fP, the \fIsdbm\fP
  117. X.CW store
  118. Xoperation will not ``wander away'' trying to split its
  119. Xdata pages to insert a datum that \fIcannot\fP (due to elaborate worst-case
  120. Xsituations) be inserted. (It will fail after a pre-defined number of attempts.)
  121. X.SH
  122. XImportant Compatibility Warning
  123. X.PP
  124. XThe \fIsdbm\fP and \fIndbm\fP
  125. Xlibraries \fIcannot\fP share databases: one cannot read the (dir/pag)
  126. Xdatabase created by the other. This is due to the differences
  127. Xbetween the \fIndbm\fP and \fIsdbm\fP algorithms\**, 
  128. X.FS
  129. XTorek's discussion [Tor87]
  130. Xindicates that \fIdbm/ndbm\fP implementations use the hash
  131. Xvalue to traverse the radix trie differently than \fIsdbm\fP
  132. Xand as a result, the page indexes are generated in \fIdifferent\fP order.
  133. XFor more information, send e-mail to the author.
  134. X.FE
  135. Xand the hash functions
  136. Xused.
  137. XIt is easy to convert between the \fIdbm/ndbm\fP databases and \fIsdbm\fP
  138. Xby ignoring the index completely: see
  139. X.CW dbd ,
  140. X.CW dbu
  141. Xetc.
  142. X.R
  143. X.LP
  144. X.SH
  145. XNotice of Intellectual Property
  146. X.LP
  147. X\fIThe entire\fP sdbm  \fIlibrary package, as authored by me,\fP Ozan S. Yigit,
  148. X\fIis hereby placed in the public domain.\fP As such, the author is not
  149. Xresponsible for the consequences of use of this software, no matter how
  150. Xawful, even if they arise from defects in it. There is no expressed or
  151. Ximplied warranty for the \fIsdbm\fP library.
  152. X.PP
  153. XSince the \fIsdbm\fP
  154. Xlibrary package is in the public domain, this \fIoriginal\fP
  155. Xrelease or any additional public-domain releases of the modified original
  156. Xcannot possibly (by definition) be withheld from you. Also by definition,
  157. XYou (singular) have all the rights to this code (including the right to
  158. Xsell without permission, the right to hoard\**
  159. X.FS
  160. XYou cannot really hoard something that is available to the public at
  161. Xlarge, but try if it makes you feel any better.
  162. X.FE
  163. Xand the right to do other icky things as
  164. Xyou see fit) but those rights are also granted to everyone else.
  165. X.PP
  166. XPlease note that all previous distributions of this software contained
  167. Xa copyright (which is now dropped) to protect its
  168. Xorigins and its current public domain status against any possible claims
  169. Xand/or challenges.
  170. X.SH
  171. XAcknowledgments
  172. X.PP
  173. XMany people have been very helpful and supportive.  A partial list would
  174. Xnecessarily include Rayan Zacherissen (who contributed the man page,
  175. Xand also hacked a MMAP version of \fIsdbm\fP),
  176. XArnold Robbins, Chris Lewis,
  177. XBill Davidsen, Henry Spencer, Geoff Collyer, Rich Salz (who got me started
  178. Xin the first place), Johannes Ruschein
  179. X(who did the minix port) and David Tilbrook. I thank you all.
  180. X.SH
  181. XDistribution Manifest and Notes
  182. X.LP
  183. XThis distribution of \fIsdbm\fP includes (at least) the following:
  184. X.P1
  185. X    CHANGES        change log
  186. X    README        this file.
  187. X    biblio        a small bibliography on external hashing
  188. X    dba.c        a crude (n/s)dbm page file analyzer
  189. X    dbd.c        a crude (n/s)dbm page file dumper (for conversion)
  190. X    dbe.1        man page for dbe.c
  191. X    dbe.c        Janick's database editor
  192. X    dbm.c        a dbm library emulation wrapper for ndbm/sdbm
  193. X    dbm.h        header file for the above
  194. X    dbu.c        a crude db management utility
  195. X    hash.c        hashing function
  196. X    makefile    guess.
  197. X    pair.c        page-level routines (posted earlier)
  198. X    pair.h        header file for the above
  199. X    readme.ms    troff source for the README file
  200. X    sdbm.3        man page
  201. X    sdbm.c        the real thing
  202. X    sdbm.h        header file for the above
  203. X    tune.h        place for tuning & portability thingies
  204. X    util.c        miscellaneous
  205. X.P2
  206. X.PP
  207. X.CW dbu
  208. Xis a simple database manipulation program\** that tries to look
  209. X.FS
  210. XThe 
  211. X.CW dbd ,
  212. X.CW dba ,
  213. X.CW dbu
  214. Xutilities are quick hacks and are not fit for production use. They were
  215. Xdeveloped late one night, just to test out \fIsdbm\fP, and convert some
  216. Xdatabases.
  217. X.FE
  218. Xlike Bell Labs'
  219. X.CW cbt
  220. Xutility. It is currently incomplete in functionality.
  221. XI use
  222. X.CW dbu
  223. Xto test out the routines: it takes (from stdin) tab separated
  224. Xkey/value pairs for commands like
  225. X.CW build
  226. Xor
  227. X.CW insert
  228. Xor takes keys for
  229. Xcommands like
  230. X.CW delete
  231. Xor
  232. X.CW look .
  233. X.P1
  234. X    dbu <build|creat|look|insert|cat|delete> dbmfile
  235. X.P2
  236. X.PP
  237. X.CW dba
  238. Xis a crude analyzer of \fIdbm/sdbm/ndbm\fP
  239. Xpage files. It scans the entire
  240. Xpage file, reporting page level statistics, and totals at the end.
  241. X.PP
  242. X.CW dbd
  243. Xis a crude dump program for \fIdbm/ndbm/sdbm\fP
  244. Xdatabases. It ignores the
  245. Xbitmap, and dumps the data pages in sequence. It can be used to create
  246. Xinput for the
  247. X.CW dbu 
  248. Xutility.
  249. XNote that
  250. X.CW dbd
  251. Xwill skip any NULLs in the key and data
  252. Xfields, thus is unsuitable to convert some peculiar databases that
  253. Xinsist in including the terminating null.
  254. X.PP
  255. XI have also included a copy of the
  256. X.CW dbe
  257. X(\fIndbm\fP DataBase Editor) by Janick Bergeron [janick@bnr.ca] for
  258. Xyour pleasure. You may find it more useful than the little
  259. X.CW dbu
  260. Xutility.
  261. X.PP
  262. X.CW dbm.[ch]
  263. Xis a \fIdbm\fP library emulation on top of \fIndbm\fP
  264. X(and hence suitable for \fIsdbm\fP). Written by Robert Elz.
  265. X.PP
  266. XThe \fIsdbm\fP
  267. Xlibrary has been around in beta test for quite a long time, and from whatever
  268. Xlittle feedback I received (maybe no news is good news), I believe it has been
  269. Xfunctioning without any significant problems. I would, of course, appreciate
  270. Xall fixes and/or improvements. Portability enhancements would especially be
  271. Xuseful.
  272. X.SH
  273. XImplementation Issues
  274. X.PP
  275. XHash functions:
  276. XThe algorithm behind \fIsdbm\fP implementation needs a good bit-scrambling
  277. Xhash function to be effective. I ran into a set of constants for a simple
  278. Xhash function that seem to help \fIsdbm\fP perform better than \fIndbm\fP
  279. Xfor various inputs:
  280. X.P1
  281. X    /*
  282. X     * polynomial conversion ignoring overflows
  283. X     * 65599 nice. 65587 even better.
  284. X     */
  285. X    long
  286. X    dbm_hash(char *str, int len) {
  287. X        register unsigned long n = 0;
  288. X    
  289. X        while (len--)
  290. X            n = n * 65599 + *str++;
  291. X        return n;
  292. X    }
  293. X.P2
  294. X.PP
  295. XThere may be better hash functions for the purposes of dynamic hashing.
  296. XTry your favorite, and check the pagefile. If it contains too many pages
  297. Xwith too many holes, (in relation to this one for example) or if
  298. X\fIsdbm\fP
  299. Xsimply stops working (fails after 
  300. X.CW SPLTMAX
  301. Xattempts to split) when you feed your
  302. XNEWS 
  303. X.CW history
  304. Xfile to it, you probably do not have a good hashing function.
  305. XIf you do better (for different types of input), I would like to know
  306. Xabout the function you use.
  307. X.PP
  308. XBlock sizes: It seems (from various tests on a few machines) that a page
  309. Xfile block size
  310. X.CW PBLKSIZ
  311. Xof 1024 is by far the best for performance, but
  312. Xthis also happens to limit the size of a key/value pair. Depending on your
  313. Xneeds, you may wish to increase the page size, and also adjust
  314. X.CW PAIRMAX
  315. X(the maximum size of a key/value pair allowed: should always be at least
  316. Xthree words smaller than
  317. X.CW PBLKSIZ .)
  318. Xaccordingly. The system-wide version of the library
  319. Xshould probably be
  320. Xconfigured with 1024 (distribution default), as this appears to be sufficient
  321. Xfor most common uses of \fIsdbm\fP.
  322. X.SH
  323. XPortability
  324. X.PP
  325. XThis package has been tested in many different UN*Xes even including minix,
  326. Xand appears to be reasonably portable. This does not mean it will port
  327. Xeasily to non-UN*X systems.
  328. X.SH
  329. XNotes and Miscellaneous
  330. X.PP
  331. XThe \fIsdbm\fP is not a very complicated package, at least not after you
  332. Xfamiliarize yourself with the literature on external hashing. There are
  333. Xother interesting algorithms in existence that ensure (approximately)
  334. Xsingle-read access to a data value associated with any key. These are
  335. Xdirectory-less schemes such as \fIlinear hashing\fP [Lit80] (+ Larson
  336. Xvariations), \fIspiral storage\fP [Mar79] or directory schemes such as
  337. X\fIextensible hashing\fP [Fag79] by Fagin et al. I do hope these sources
  338. Xprovide a reasonable playground for experimentation with other algorithms.
  339. XSee the June 1988 issue of ACM Computing Surveys [Enb88] for an
  340. Xexcellent overview of the field. 
  341. X.PG
  342. X.SH
  343. XReferences
  344. X.LP
  345. X.IP [Lar78] 4m
  346. XP.-A. Larson,
  347. X``Dynamic Hashing'', \fIBIT\fP, vol.  18,  pp. 184-201, 1978.
  348. X.IP [Tho90] 4m
  349. XKen Thompson, \fIprivate communication\fP, Nov. 1990
  350. X.IP [Lit80] 4m
  351. XW. Litwin,
  352. X`` Linear Hashing: A new tool  for  file  and table addressing'',
  353. X\fIProceedings of the 6th Conference on Very Large  Dabatases  (Montreal)\fP,
  354. Xpp.  212-223,  Very Large Database Foundation, Saratoga, Calif., 1980.
  355. X.IP [Fag79] 4m
  356. XR. Fagin, J.  Nievergelt,  N.  Pippinger,  and  H.  R. Strong,
  357. X``Extendible Hashing - A Fast Access Method for Dynamic Files'',
  358. X\fIACM Trans. Database Syst.\fP, vol. 4,  no.3, pp. 315-344, Sept. 1979.
  359. X.IP [Wal84] 4m
  360. XRich Wales,
  361. X``Discussion of "dbm" data base system'', \fIUSENET newsgroup unix.wizards\fP,
  362. XJan. 1984.
  363. X.IP [Tor87] 4m
  364. XChris Torek,
  365. X``Re:  dbm.a  and  ndbm.a  archives'', \fIUSENET newsgroup comp.unix\fP,
  366. X1987.
  367. X.IP [Mar79] 4m
  368. XG. N. Martin,
  369. X``Spiral Storage: Incrementally  Augmentable  Hash  Addressed  Storage'',
  370. X\fITechnical Report #27\fP, University of Varwick, Coventry, U.K., 1979.
  371. X.IP [Enb88] 4m
  372. XR. J. Enbody and H. C. Du,
  373. X``Dynamic Hashing  Schemes'',\fIACM Computing Surveys\fP,
  374. Xvol. 20, no. 2, pp. 85-113, June 1988.
  375. END_OF_FILE
  376. if test 11691 -ne `wc -c <'dist/readme.ms'`; then
  377.     echo shar: \"'dist/readme.ms'\" unpacked with wrong size!
  378. fi
  379. # end of 'dist/readme.ms'
  380. fi
  381. if test -f 'dist/sdbm.c' -a "${1}" != "-c" ; then 
  382.   echo shar: Will not clobber existing file \"'dist/sdbm.c'\"
  383. else
  384. echo shar: Extracting \"'dist/sdbm.c'\" \(11006 characters\)
  385. sed "s/^X//" >'dist/sdbm.c' <<'END_OF_FILE'
  386. X/*
  387. X * sdbm - ndbm work-alike hashed database library
  388. X * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
  389. X * author: oz@nexus.yorku.ca
  390. X * status: public domain.
  391. X *
  392. X * core routines
  393. X */
  394. X
  395. X#ifndef lint
  396. Xstatic char rcsid[] = "$Id: sdbm.c,v 1.16 90/12/13 13:01:31 oz Exp $";
  397. X#endif
  398. X
  399. X#include "sdbm.h"
  400. X#include "tune.h"
  401. X#include "pair.h"
  402. X
  403. X#include <sys/types.h>
  404. X#include <sys/stat.h>
  405. X#ifdef BSD42
  406. X#include <sys/file.h>
  407. X#else
  408. X#include <fcntl.h>
  409. X#include <memory.h>
  410. X#endif
  411. X#include <errno.h>
  412. X#include <string.h>
  413. X
  414. X#ifdef __STDC__
  415. X#include <stddef.h>
  416. X#endif
  417. X
  418. X#ifndef NULL
  419. X#define NULL    0
  420. X#endif
  421. X
  422. X/*
  423. X * externals
  424. X */
  425. X#ifndef sun
  426. Xextern int errno;
  427. X#endif
  428. X
  429. Xextern char *malloc proto((unsigned int));
  430. Xextern void free proto((void *));
  431. Xextern long lseek();
  432. X
  433. X/*
  434. X * forward
  435. X */
  436. Xstatic int getdbit proto((DBM *, long));
  437. Xstatic int setdbit proto((DBM *, long));
  438. Xstatic int getpage proto((DBM *, long));
  439. Xstatic datum getnext proto((DBM *));
  440. Xstatic int makroom proto((DBM *, long, int));
  441. X
  442. X/*
  443. X * useful macros
  444. X */
  445. X#define bad(x)        ((x).dptr == NULL || (x).dsize <= 0)
  446. X#define exhash(item)    dbm_hash((item).dptr, (item).dsize)
  447. X#define ioerr(db)    ((db)->flags |= DBM_IOERR)
  448. X
  449. X#define OFF_PAG(off)    (long) (off) * PBLKSIZ
  450. X#define OFF_DIR(off)    (long) (off) * DBLKSIZ
  451. X
  452. Xstatic long masks[] = {
  453. X    000000000000, 000000000001, 000000000003, 000000000007,
  454. X    000000000017, 000000000037, 000000000077, 000000000177,
  455. X    000000000377, 000000000777, 000000001777, 000000003777,
  456. X    000000007777, 000000017777, 000000037777, 000000077777,
  457. X    000000177777, 000000377777, 000000777777, 000001777777,
  458. X    000003777777, 000007777777, 000017777777, 000037777777,
  459. X    000077777777, 000177777777, 000377777777, 000777777777,
  460. X    001777777777, 003777777777, 007777777777, 017777777777
  461. X};
  462. X
  463. Xdatum nullitem = {NULL, 0};
  464. X
  465. XDBM *
  466. Xdbm_open(file, flags, mode)
  467. Xregister char *file;
  468. Xregister int flags;
  469. Xregister int mode;
  470. X{
  471. X    register DBM *db;
  472. X    register char *dirname;
  473. X    register char *pagname;
  474. X    register int n;
  475. X
  476. X    if (file == NULL || !*file)
  477. X        return errno = EINVAL, (DBM *) NULL;
  478. X/*
  479. X * need space for two seperate filenames
  480. X */
  481. X    n = strlen(file) * 2 + strlen(DIRFEXT) + strlen(PAGFEXT) + 2;
  482. X
  483. X    if ((dirname = malloc((unsigned) n)) == NULL)
  484. X        return errno = ENOMEM, (DBM *) NULL;
  485. X/*
  486. X * build the file names
  487. X */
  488. X    dirname = strcat(strcpy(dirname, file), DIRFEXT);
  489. X    pagname = strcpy(dirname + strlen(dirname) + 1, file);
  490. X    pagname = strcat(pagname, PAGFEXT);
  491. X
  492. X    db = dbm_prep(dirname, pagname, flags, mode);
  493. X    free((char *) dirname);
  494. X    return db;
  495. X}
  496. X
  497. XDBM *
  498. Xdbm_prep(dirname, pagname, flags, mode)
  499. Xchar *dirname;
  500. Xchar *pagname;
  501. Xint flags;
  502. Xint mode;
  503. X{
  504. X    register DBM *db;
  505. X    struct stat dstat;
  506. X
  507. X    if ((db = (DBM *) malloc(sizeof(DBM))) == NULL)
  508. X        return errno = ENOMEM, (DBM *) NULL;
  509. X
  510. X        db->flags = 0;
  511. X        db->hmask = 0;
  512. X        db->blkptr = 0;
  513. X        db->keyptr = 0;
  514. X/*
  515. X * adjust user flags so that WRONLY becomes RDWR, 
  516. X * as required by this package. Also set our internal
  517. X * flag for RDONLY.
  518. X */
  519. X    if (flags & O_WRONLY)
  520. X        flags = (flags & ~O_WRONLY) | O_RDWR;
  521. X    if (flags & O_RDONLY)
  522. X        db->flags = DBM_RDONLY;
  523. X/*
  524. X * open the files in sequence, and stat the dirfile.
  525. X * If we fail anywhere, undo everything, return NULL.
  526. X */
  527. X    if ((db->pagf = open(pagname, flags, mode)) > -1) {
  528. X        if ((db->dirf = open(dirname, flags, mode)) > -1) {
  529. X/*
  530. X * need the dirfile size to establish max bit number.
  531. X */
  532. X            if (fstat(db->dirf, &dstat) == 0) {
  533. X/*
  534. X * zero size: either a fresh database, or one with a single,
  535. X * unsplit data page: dirpage is all zeros.
  536. X */
  537. X                db->dirbno = (!dstat.st_size) ? 0 : -1;
  538. X                db->pagbno = -1;
  539. X                db->maxbno = dstat.st_size * BYTESIZ;
  540. X
  541. X                (void) memset(db->pagbuf, 0, PBLKSIZ);
  542. X                (void) memset(db->dirbuf, 0, DBLKSIZ);
  543. X            /*
  544. X             * success
  545. X             */
  546. X                return db;
  547. X            }
  548. X            (void) close(db->dirf);
  549. X        }
  550. X        (void) close(db->pagf);
  551. X    }
  552. X    free((char *) db);
  553. X    return (DBM *) NULL;
  554. X}
  555. X
  556. Xvoid
  557. Xdbm_close(db)
  558. Xregister DBM *db;
  559. X{
  560. X    if (db == NULL)
  561. X        errno = EINVAL;
  562. X    else {
  563. X        (void) close(db->dirf);
  564. X        (void) close(db->pagf);
  565. X        free((char *) db);
  566. X    }
  567. X}
  568. X
  569. Xdatum
  570. Xdbm_fetch(db, key)
  571. Xregister DBM *db;
  572. Xdatum key;
  573. X{
  574. X    if (db == NULL || bad(key))
  575. X        return errno = EINVAL, nullitem;
  576. X
  577. X    if (getpage(db, exhash(key)))
  578. X        return getpair(db->pagbuf, key);
  579. X
  580. X    return ioerr(db), nullitem;
  581. X}
  582. X
  583. Xint
  584. Xdbm_delete(db, key)
  585. Xregister DBM *db;
  586. Xdatum key;
  587. X{
  588. X    if (db == NULL || bad(key))
  589. X        return errno = EINVAL, -1;
  590. X    if (dbm_rdonly(db))
  591. X        return errno = EPERM, -1;
  592. X
  593. X    if (getpage(db, exhash(key))) {
  594. X        if (!delpair(db->pagbuf, key))
  595. X            return -1;
  596. X/*
  597. X * update the page file
  598. X */
  599. X        if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
  600. X            || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
  601. X            return ioerr(db), -1;
  602. X
  603. X        return 0;
  604. X    }
  605. X
  606. X    return ioerr(db), -1;
  607. X}
  608. X
  609. Xint
  610. Xdbm_store(db, key, val, flags)
  611. Xregister DBM *db;
  612. Xdatum key;
  613. Xdatum val;
  614. Xint flags;
  615. X{
  616. X    int need;
  617. X    register long hash;
  618. X
  619. X    if (db == NULL || bad(key))
  620. X        return errno = EINVAL, -1;
  621. X    if (dbm_rdonly(db))
  622. X        return errno = EPERM, -1;
  623. X
  624. X    need = key.dsize + val.dsize;
  625. X/*
  626. X * is the pair too big (or too small) for this database ??
  627. X */
  628. X    if (need < 0 || need > PAIRMAX)
  629. X        return errno = EINVAL, -1;
  630. X
  631. X    if (getpage(db, (hash = exhash(key)))) {
  632. X/*
  633. X * if we need to replace, delete the key/data pair
  634. X * first. If it is not there, ignore.
  635. X */
  636. X        if (flags == DBM_REPLACE)
  637. X            (void) delpair(db->pagbuf, key);
  638. X#ifdef SEEDUPS
  639. X        else if (duppair(db->pagbuf, key))
  640. X            return 1;
  641. X#endif
  642. X/*
  643. X * if we do not have enough room, we have to split.
  644. X */
  645. X        if (!fitpair(db->pagbuf, need))
  646. X            if (!makroom(db, hash, need))
  647. X                return ioerr(db), -1;
  648. X/*
  649. X * we have enough room or split is successful. insert the key,
  650. X * and update the page file.
  651. X */
  652. X        (void) putpair(db->pagbuf, key, val);
  653. X
  654. X        if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
  655. X            || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
  656. X            return ioerr(db), -1;
  657. X    /*
  658. X     * success
  659. X     */
  660. X        return 0;
  661. X    }
  662. X
  663. X    return ioerr(db), -1;
  664. X}
  665. X
  666. X/*
  667. X * makroom - make room by splitting the overfull page
  668. X * this routine will attempt to make room for SPLTMAX times before
  669. X * giving up.
  670. X */
  671. Xstatic int
  672. Xmakroom(db, hash, need)
  673. Xregister DBM *db;
  674. Xlong hash;
  675. Xint need;
  676. X{
  677. X    long newp;
  678. X    char twin[PBLKSIZ];
  679. X    char *pag = db->pagbuf;
  680. X    char *new = twin;
  681. X    register int smax = SPLTMAX;
  682. X
  683. X    do {
  684. X/*
  685. X * split the current page
  686. X */
  687. X        (void) splpage(pag, new, db->hmask + 1);
  688. X/*
  689. X * address of the new page
  690. X */
  691. X        newp = (hash & db->hmask) | (db->hmask + 1);
  692. X
  693. X/*
  694. X * write delay, read avoidence/cache shuffle:
  695. X * select the page for incoming pair: if key is to go to the new page,
  696. X * write out the previous one, and copy the new one over, thus making
  697. X * it the current page. If not, simply write the new page, and we are
  698. X * still looking at the page of interest. current page is not updated
  699. X * here, as dbm_store will do so, after it inserts the incoming pair.
  700. X */
  701. X        if (hash & (db->hmask + 1)) {
  702. X            if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
  703. X                || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
  704. X                return 0;
  705. X            db->pagbno = newp;
  706. X            (void) memcpy(pag, new, PBLKSIZ);
  707. X        }
  708. X        else if (lseek(db->pagf, OFF_PAG(newp), SEEK_SET) < 0
  709. X             || write(db->pagf, new, PBLKSIZ) < 0)
  710. X            return 0;
  711. X
  712. X        if (!setdbit(db, db->curbit))
  713. X            return 0;
  714. X/*
  715. X * see if we have enough room now
  716. X */
  717. X        if (fitpair(pag, need))
  718. X            return 1;
  719. X/*
  720. X * try again... update curbit and hmask as getpage would have
  721. X * done. because of our update of the current page, we do not
  722. X * need to read in anything. BUT we have to write the current
  723. X * [deferred] page out, as the window of failure is too great.
  724. X */
  725. X        db->curbit = 2 * db->curbit + 
  726. X            (hash & (db->hmask + 1)) ? 2 : 1;
  727. X        db->hmask |= (db->hmask + 1);
  728. X
  729. X        if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
  730. X            || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
  731. X            return 0;
  732. X
  733. X    } while (--smax);
  734. X/*
  735. X * if we are here, this is real bad news. After SPLTMAX splits,
  736. X * we still cannot fit the key. say goodnight.
  737. X */
  738. X#ifdef BADMESS
  739. X    (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
  740. X#endif
  741. X    return 0;
  742. X
  743. X}
  744. X
  745. X/*
  746. X * the following two routines will break if
  747. X * deletions aren't taken into account. (ndbm bug)
  748. X */
  749. Xdatum
  750. Xdbm_firstkey(db)
  751. Xregister DBM *db;
  752. X{
  753. X    if (db == NULL)
  754. X        return errno = EINVAL, nullitem;
  755. X/*
  756. X * start at page 0
  757. X */
  758. X    if (lseek(db->pagf, OFF_PAG(0), SEEK_SET) < 0
  759. X        || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
  760. X        return ioerr(db), nullitem;
  761. X    db->pagbno = 0;
  762. X    db->blkptr = 0;
  763. X    db->keyptr = 0;
  764. X
  765. X    return getnext(db);
  766. X}
  767. X
  768. Xdatum
  769. Xdbm_nextkey(db)
  770. Xregister DBM *db;
  771. X{
  772. X    if (db == NULL)
  773. X        return errno = EINVAL, nullitem;
  774. X    return getnext(db);
  775. X}
  776. X
  777. X/*
  778. X * all important binary trie traversal
  779. X */
  780. Xstatic int
  781. Xgetpage(db, hash)
  782. Xregister DBM *db;
  783. Xregister long hash;
  784. X{
  785. X    register int hbit;
  786. X    register long dbit;
  787. X    register long pagb;
  788. X
  789. X    dbit = 0;
  790. X    hbit = 0;
  791. X    while (dbit < db->maxbno && getdbit(db, dbit))
  792. X        dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);
  793. X
  794. X    debug(("dbit: %d...", dbit));
  795. X
  796. X    db->curbit = dbit;
  797. X    db->hmask = masks[hbit];
  798. X
  799. X    pagb = hash & db->hmask;
  800. X/*
  801. X * see if the block we need is already in memory.
  802. X * note: this lookaside cache has about 10% hit rate.
  803. X */
  804. X    if (pagb != db->pagbno) { 
  805. X/*
  806. X * note: here, we assume a "hole" is read as 0s.
  807. X * if not, must zero pagbuf first.
  808. X */
  809. X        if (lseek(db->pagf, OFF_PAG(pagb), SEEK_SET) < 0
  810. X            || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
  811. X            return 0;
  812. X        if (!chkpage(db->pagbuf))
  813. X            return 0;
  814. X        db->pagbno = pagb;
  815. X
  816. X        debug(("pag read: %d\n", pagb));
  817. X    }
  818. X    return 1;
  819. X}
  820. X
  821. Xstatic int
  822. Xgetdbit(db, dbit)
  823. Xregister DBM *db;
  824. Xregister long dbit;
  825. X{
  826. X    register long c;
  827. X    register long dirb;
  828. X
  829. X    c = dbit / BYTESIZ;
  830. X    dirb = c / DBLKSIZ;
  831. X
  832. X    if (dirb != db->dirbno) {
  833. X        if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
  834. X            || read(db->dirf, db->dirbuf, DBLKSIZ) < 0)
  835. X            return 0;
  836. X        db->dirbno = dirb;
  837. X
  838. X        debug(("dir read: %d\n", dirb));
  839. X    }
  840. X
  841. X    return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ);
  842. X}
  843. X
  844. Xstatic int
  845. Xsetdbit(db, dbit)
  846. Xregister DBM *db;
  847. Xregister long dbit;
  848. X{
  849. X    register long c;
  850. X    register long dirb;
  851. X
  852. X    c = dbit / BYTESIZ;
  853. X    dirb = c / DBLKSIZ;
  854. X
  855. X    if (dirb != db->dirbno) {
  856. X        if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
  857. X            || read(db->dirf, db->dirbuf, DBLKSIZ) < 0)
  858. X            return 0;
  859. X        db->dirbno = dirb;
  860. X
  861. X        debug(("dir read: %d\n", dirb));
  862. X    }
  863. X
  864. X    db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
  865. X
  866. X    if (dbit >= db->maxbno)
  867. X        db->maxbno += DBLKSIZ * BYTESIZ;
  868. X
  869. X    if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
  870. X        || write(db->dirf, db->dirbuf, DBLKSIZ) < 0)
  871. X        return 0;
  872. X
  873. X    return 1;
  874. X}
  875. X
  876. X/*
  877. X * getnext - get the next key in the page, and if done with
  878. X * the page, try the next page in sequence
  879. X */
  880. Xstatic datum
  881. Xgetnext(db)
  882. Xregister DBM *db;
  883. X{
  884. X    datum key;
  885. X
  886. X    for (;;) {
  887. X        db->keyptr++;
  888. X        key = getnkey(db->pagbuf, db->keyptr);
  889. X        if (key.dptr != NULL)
  890. X            return key;
  891. X/*
  892. X * we either run out, or there is nothing on this page..
  893. X * try the next one... If we lost our position on the
  894. X * file, we will have to seek.
  895. X */
  896. X        db->keyptr = 0;
  897. X        if (db->pagbno != db->blkptr++)
  898. X            if (lseek(db->pagf, OFF_PAG(db->blkptr), SEEK_SET) < 0)
  899. X                break;
  900. X        db->pagbno = db->blkptr;
  901. X        if (read(db->pagf, db->pagbuf, PBLKSIZ) <= 0)
  902. X            break;
  903. X        if (!chkpage(db->pagbuf))
  904. X            break;
  905. X    }
  906. X
  907. X    return ioerr(db), nullitem;
  908. X}
  909. END_OF_FILE
  910. if test 11006 -ne `wc -c <'dist/sdbm.c'`; then
  911.     echo shar: \"'dist/sdbm.c'\" unpacked with wrong size!
  912. fi
  913. # end of 'dist/sdbm.c'
  914. fi
  915. echo shar: End of archive 2 \(of 2\).
  916. cp /dev/null ark2isdone
  917. MISSING=""
  918. for I in 1 2 ; do
  919.     if test ! -f ark${I}isdone ; then
  920.     MISSING="${MISSING} ${I}"
  921.     fi
  922. done
  923. if test "${MISSING}" = "" ; then
  924.     echo You have unpacked both archives.
  925.     rm -f ark[1-9]isdone
  926. else
  927.     echo You still need to unpack the following archives:
  928.     echo "        " ${MISSING}
  929. fi
  930. ##  End of shell archive.
  931. exit 0
  932.