* tree.c (make_lang_type_fn): New funtion pointer.
[official-gcc.git] / libio / dbz / dbz.c
blob7489a09d15ae5ed5ecc636bdc0eda62de40e01a5
1 /*
3 dbz.c V3.2
5 Copyright 1988 Jon Zeeff (zeeff@b-tech.ann-arbor.mi.us)
6 You can use this code in any manner, as long as you leave my name on it
7 and don't hold me responsible for any problems with it.
9 Hacked on by gdb@ninja.UUCP (David Butler); Sun Jun 5 00:27:08 CDT 1988
11 Various improvments + INCORE by moraes@ai.toronto.edu (Mark Moraes)
13 Major reworking by Henry Spencer as part of the C News project.
15 These routines replace dbm as used by the usenet news software
16 (it's not a full dbm replacement by any means). It's fast and
17 simple. It contains no AT&T code.
19 In general, dbz's files are 1/20 the size of dbm's. Lookup performance
20 is somewhat better, while file creation is spectacularly faster, especially
21 if the incore facility is used.
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #ifndef __STDC__
31 extern int errno;
32 #endif
33 #include <dbz.h>
36 * #ifdef index. "LIA" = "leave it alone unless you know what you're doing".
38 * FUNNYSEEKS SEEK_SET is not 0, get it from <unistd.h>
39 * INDEX_SIZE backward compatibility with old dbz; avoid using this
40 * NMEMORY number of days of memory for use in sizing new table (LIA)
41 * INCORE backward compatibility with old dbz; use dbzincore() instead
42 * DBZDEBUG enable debugging
43 * DEFSIZE default table size (not as critical as in old dbz)
44 * OLDBNEWS default case mapping as in old B News; set NOBUFFER
45 * BNEWS default case mapping as in current B News; set NOBUFFER
46 * DEFCASE default case-map algorithm selector
47 * NOTAGS fseek offsets are strange, do not do tagging (see below)
48 * NPAGBUF size of .pag buffer, in longs (LIA)
49 * SHISTBUF size of ASCII-file buffer, in bytes (LIA)
50 * MAXRUN length of run which shifts to next table (see below) (LIA)
51 * OVERFLOW long-int arithmetic overflow must be avoided, will trap
52 * NOBUFFER do not buffer hash-table i/o, B News locking is defective
55 #ifdef FUNNYSEEKS
56 #include <unistd.h>
57 #else
58 #define SEEK_SET 0
59 #endif
60 #ifdef OVERFLOW
61 #include <limits.h>
62 #endif
64 static int dbzversion = 3; /* for validating .dir file format */
67 * The dbz database exploits the fact that when news stores a <key,value>
68 * tuple, the `value' part is a seek offset into a text file, pointing to
69 * a copy of the `key' part. This avoids the need to store a copy of
70 * the key in the dbz files. However, the text file *must* exist and be
71 * consistent with the dbz files, or things will fail.
73 * The basic format of the database is a simple hash table containing the
74 * values. A value is stored by indexing into the table using a hash value
75 * computed from the key; collisions are resolved by linear probing (just
76 * search forward for an empty slot, wrapping around to the beginning of
77 * the table if necessary). Linear probing is a performance disaster when
78 * the table starts to get full, so a complication is introduced. The
79 * database is actually one *or more* tables, stored sequentially in the
80 * .pag file, and the length of linear-probe sequences is limited. The
81 * search (for an existing item or an empty slot) always starts in the
82 * first table, and whenever MAXRUN probes have been done in table N,
83 * probing continues in table N+1. This behaves reasonably well even in
84 * cases of massive overflow. There are some other small complications
85 * added, see comments below.
87 * The table size is fixed for any particular database, but is determined
88 * dynamically when a database is rebuilt. The strategy is to try to pick
89 * the size so the first table will be no more than 2/3 full, that being
90 * slightly before the point where performance starts to degrade. (It is
91 * desirable to be a bit conservative because the overflow strategy tends
92 * to produce files with holes in them, which is a nuisance.)
96 * The following is for backward compatibility.
98 #ifdef INDEX_SIZE
99 #define DEFSIZE INDEX_SIZE
100 #endif
103 * ANSI C says the offset argument to fseek is a long, not an off_t, for some
104 * reason. Let's use off_t anyway.
106 #define SOF (sizeof(off_t))
109 * We assume that unused areas of a binary file are zeros, and that the
110 * bit pattern of `(off_t)0' is all zeros. The alternative is rather
111 * painful file initialization. Note that okayvalue(), if OVERFLOW is
112 * defined, knows what value of an offset would cause overflow.
114 #define VACANT ((off_t)0)
115 #define BIAS(o) ((o)+1) /* make any valid off_t non-VACANT */
116 #define UNBIAS(o) ((o)-1) /* reverse BIAS() effect */
119 * In a Unix implementation, or indeed any in which an off_t is a byte
120 * count, there are a bunch of high bits free in an off_t. There is a
121 * use for them. Checking a possible hit by looking it up in the base
122 * file is relatively expensive, and the cost can be dramatically reduced
123 * by using some of those high bits to tag the value with a few more bits
124 * of the key's hash. This detects most false hits without the overhead of
125 * seek+read+strcmp. We use the top bit to indicate whether the value is
126 * tagged or not, and don't tag a value which is using the tag bits itself.
127 * We're in trouble if the off_t representation wants to use the top bit.
128 * The actual bitmasks and offset come from the configuration stuff,
129 * which permits fiddling with them as necessary, and also suppressing
130 * them completely (by defining the masks to 0). We build pre-shifted
131 * versions of the masks for efficiency.
133 static off_t tagbits; /* pre-shifted tag mask */
134 static off_t taghere; /* pre-shifted tag-enable bit */
135 static off_t tagboth; /* tagbits|taghere */
136 #define HASTAG(o) ((o)&taghere)
137 #define TAG(o) ((o)&tagbits)
138 #define NOTAG(o) ((o)&~tagboth)
139 #define CANTAG(o) (((o)&tagboth) == 0)
140 #define MKTAG(v) (((v)<<conf.tagshift)&tagbits)
143 * A new, from-scratch database, not built as a rebuild of an old one,
144 * needs to know table size, casemap algorithm, and tagging. Normally
145 * the user supplies this info, but there have to be defaults.
147 #ifndef DEFSIZE
148 #define DEFSIZE 120011 /* 300007 might be better */
149 #endif
150 #ifdef OLDBNEWS
151 #define DEFCASE '0' /* B2.10 -- no mapping */
152 #define NOBUFFER /* B News locking is defective */
153 #endif
154 #ifdef BNEWS
155 #define DEFCASE '=' /* B2.11 -- all mapped */
156 #define NOBUFFER /* B News locking is defective */
157 #endif
158 #ifndef DEFCASE /* C News compatibility is the default */
159 #define DEFCASE 'C' /* C News -- RFC822 mapping */
160 #endif
161 #ifndef NOTAGS
162 #define TAGENB 0x80 /* tag enable is top bit, tag is next 7 */
163 #define TAGMASK 0x7f
164 #define TAGSHIFT 24
165 #else
166 #define TAGENB 0 /* no tags */
167 #define TAGMASK 0
168 #define TAGSHIFT 0
169 #endif
172 * We read configuration info from the .dir file into this structure,
173 * so we can avoid wired-in assumptions for an existing database.
175 * Among the info is a record of recent peak usages, so that a new table
176 * size can be chosen intelligently when rebuilding. 10 is a good
177 * number of usages to keep, since news displays marked fluctuations
178 * in volume on a 7-day cycle.
180 struct dbzconfig {
181 int olddbz; /* .dir file empty but .pag not? */
182 off_t tsize; /* table size */
183 # ifndef NMEMORY
184 # define NMEMORY 10 /* # days of use info to remember */
185 # endif
186 # define NUSEDS (1+NMEMORY)
187 off_t used[NUSEDS]; /* entries used today, yesterday, ... */
188 int valuesize; /* size of table values, == SOF */
189 int bytemap[SOF]; /* byte-order map */
190 char casemap; /* case-mapping algorithm (see cipoint()) */
191 char fieldsep; /* field separator in base file, if any */
192 off_t tagenb; /* unshifted tag-enable bit */
193 off_t tagmask; /* unshifted tag mask */
194 int tagshift; /* shift count for tagmask and tagenb */
196 static struct dbzconfig conf;
197 static int getconf();
198 static long getno();
199 static int putconf();
200 static void mybytemap();
201 static off_t bytemap();
204 * For a program that makes many, many references to the database, it
205 * is a large performance win to keep the table in core, if it will fit.
206 * Note that this does hurt robustness in the event of crashes, and
207 * dbmclose() *must* be called to flush the in-core database to disk.
208 * The code is prepared to deal with the possibility that there isn't
209 * enough memory. There *is* an assumption that a size_t is big enough
210 * to hold the size (in bytes) of one table, so dbminit() tries to figure
211 * out whether this is possible first.
213 * The preferred way to ask for an in-core table is to do dbzincore(1)
214 * before dbminit(). The default is not to do it, although -DINCORE
215 * overrides this for backward compatibility with old dbz.
217 * We keep only the first table in core. This greatly simplifies the
218 * code, and bounds memory demand. Furthermore, doing this is a large
219 * performance win even in the event of massive overflow.
221 #ifdef INCORE
222 static int incore = 1;
223 #else
224 static int incore = 0;
225 #endif
228 * Stdio buffer for .pag reads. Buffering more than about 16 does not help
229 * significantly at the densities we try to maintain, and the much larger
230 * buffers that most stdios default to are much more expensive to fill.
231 * With small buffers, stdio is performance-competitive with raw read(),
232 * and it's much more portable.
234 #ifndef NPAGBUF
235 #define NPAGBUF 16
236 #endif
237 #ifndef NOBUFFER
238 #ifdef _IOFBF
239 static off_t pagbuf[NPAGBUF]; /* only needed if !NOBUFFER && _IOFBF */
240 #endif
241 #endif
244 * Stdio buffer for base-file reads. Message-IDs (all news ever needs to
245 * read) are essentially never longer than 64 bytes, and the typical stdio
246 * buffer is so much larger that it is much more expensive to fill.
248 #ifndef SHISTBUF
249 #define SHISTBUF 64
250 #endif
251 #ifdef _IOFBF
252 static char basebuf[SHISTBUF]; /* only needed if _IOFBF exists */
253 #endif
256 * Data structure for recording info about searches.
258 struct searcher {
259 off_t place; /* current location in file */
260 int tabno; /* which table we're in */
261 int run; /* how long we'll stay in this table */
262 # ifndef MAXRUN
263 # define MAXRUN 100
264 # endif
265 long hash; /* the key's hash code (for optimization) */
266 off_t tag; /* tag we are looking for */
267 int seen; /* have we examined current location? */
268 int aborted; /* has i/o error aborted search? */
270 static void start();
271 #define FRESH ((struct searcher *)NULL)
272 static off_t search();
273 #define NOTFOUND ((off_t)-1)
274 static int okayvalue();
275 static int set();
278 * Arguably the searcher struct for a given routine ought to be local to
279 * it, but a fetch() is very often immediately followed by a store(), and
280 * in some circumstances it is a useful performance win to remember where
281 * the fetch() completed. So we use a global struct and remember whether
282 * it is current.
284 static struct searcher srch;
285 static struct searcher *prevp; /* &srch or FRESH */
287 /* byte-ordering stuff */
288 static int mybmap[SOF]; /* my byte order (see mybytemap()) */
289 static int bytesame; /* is database order same as mine? */
290 #define MAPIN(o) ((bytesame) ? (o) : bytemap((o), conf.bytemap, mybmap))
291 #define MAPOUT(o) ((bytesame) ? (o) : bytemap((o), mybmap, conf.bytemap))
294 * The double parentheses needed to make this work are ugly, but the
295 * alternative (under most compilers) is to pack around 2K of unused
296 * strings -- there's just no way to get rid of them.
298 static int debug; /* controlled by dbzdebug() */
299 #ifdef DBZDEBUG
300 #define DEBUG(args) if (debug) { (void) printf args ; }
301 #else
302 #define DEBUG(args) ;
303 #endif
305 /* externals used */
306 extern char *malloc();
307 extern char *calloc();
308 extern void free(); /* ANSI C; some old implementations say int */
309 extern int atoi();
310 extern long atol();
312 /* misc. forwards */
313 static long hash();
314 static void crcinit();
315 static char *cipoint();
316 static char *mapcase();
317 static int isprime();
318 static FILE *latebase();
320 /* file-naming stuff */
321 static char dir[] = ".dir";
322 static char pag[] = ".pag";
323 static char *enstring();
325 /* central data structures */
326 static FILE *basef; /* descriptor for base file */
327 static char *basefname; /* name for not-yet-opened base file */
328 static FILE *dirf; /* descriptor for .dir file */
329 static int dirronly; /* dirf open read-only? */
330 static FILE *pagf = NULL; /* descriptor for .pag file */
331 static off_t pagpos; /* posn in pagf; only search may set != -1 */
332 static int pagronly; /* pagf open read-only? */
333 static off_t *corepag; /* incore version of .pag file, if any */
334 static FILE *bufpagf; /* well-buffered pagf, for incore rewrite */
335 static off_t *getcore();
336 static int putcore();
337 static int written; /* has a store() been done? */
340 - dbzfresh - set up a new database, no historical info
342 int /* 0 success, -1 failure */
343 dbzfresh(name, size, fs, cmap, tagmask)
344 char *name; /* base name; .dir and .pag must exist */
345 long size; /* table size (0 means default) */
346 int fs; /* field-separator character in base file */
347 int cmap; /* case-map algorithm (0 means default) */
348 off_t tagmask; /* 0 default, 1 no tags */
350 register char *fn;
351 struct dbzconfig c;
352 register off_t m;
353 register FILE *f;
355 if (pagf != NULL) {
356 DEBUG(("dbzfresh: database already open\n"));
357 return(-1);
359 if (size != 0 && size < 2) {
360 DEBUG(("dbzfresh: preposterous size (%ld)\n", size));
361 return(-1);
364 /* get default configuration */
365 if (getconf((FILE *)NULL, (FILE *)NULL, &c) < 0)
366 return(-1); /* "can't happen" */
368 /* and mess with it as specified */
369 if (size != 0)
370 c.tsize = size;
371 c.fieldsep = fs;
372 switch (cmap) {
373 case 0:
374 case '0':
375 case 'B': /* 2.10 compat */
376 c.casemap = '0'; /* '\0' nicer, but '0' printable! */
377 break;
378 case '=':
379 case 'b': /* 2.11 compat */
380 c.casemap = '=';
381 break;
382 case 'C':
383 c.casemap = 'C';
384 break;
385 case '?':
386 c.casemap = DEFCASE;
387 break;
388 default:
389 DEBUG(("dbzfresh case map `%c' unknown\n", cmap));
390 return(-1);
391 break;
393 switch (tagmask) {
394 case 0: /* default */
395 break;
396 case 1: /* no tags */
397 c.tagshift = 0;
398 c.tagmask = 0;
399 c.tagenb = 0;
400 break;
401 default:
402 m = tagmask;
403 c.tagshift = 0;
404 while (!(m&01)) {
405 m >>= 1;
406 c.tagshift++;
408 c.tagmask = m;
409 c.tagenb = (m << 1) & ~m;
410 break;
413 /* write it out */
414 fn = enstring(name, dir);
415 if (fn == NULL)
416 return(-1);
417 f = fopen(fn, "w");
418 free(fn);
419 if (f == NULL) {
420 DEBUG(("dbzfresh: unable to write config\n"));
421 return(-1);
423 if (putconf(f, &c) < 0) {
424 (void) fclose(f);
425 return(-1);
427 if (fclose(f) == EOF) {
428 DEBUG(("dbzfresh: fclose failure\n"));
429 return(-1);
432 /* create/truncate .pag */
433 fn = enstring(name, pag);
434 if (fn == NULL)
435 return(-1);
436 f = fopen(fn, "w");
437 free(fn);
438 if (f == NULL) {
439 DEBUG(("dbzfresh: unable to create/truncate .pag file\n"));
440 return(-1);
441 } else
442 (void) fclose(f);
444 /* and punt to dbminit for the hard work */
445 return(dbminit(name));
449 - dbzsize - what's a good table size to hold this many entries?
451 long
452 dbzsize(contents)
453 long contents; /* 0 means what's the default */
455 register long n;
457 if (contents <= 0) { /* foulup or default inquiry */
458 DEBUG(("dbzsize: preposterous input (%ld)\n", contents));
459 return(DEFSIZE);
461 n = (contents/2)*3; /* try to keep table at most 2/3 full */
462 if (!(n&01)) /* make it odd */
463 n++;
464 DEBUG(("dbzsize: tentative size %ld\n", n));
465 while (!isprime(n)) /* and look for a prime */
466 n += 2;
467 DEBUG(("dbzsize: final size %ld\n", n));
469 return(n);
473 - isprime - is a number prime?
475 * This is not a terribly efficient approach.
477 static int /* predicate */
478 isprime(x)
479 register long x;
481 static int quick[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 0 };
482 register int *ip;
483 register long div;
484 register long stop;
486 /* hit the first few primes quickly to eliminate easy ones */
487 /* this incidentally prevents ridiculously small tables */
488 for (ip = quick; (div = *ip) != 0; ip++)
489 if (x%div == 0) {
490 DEBUG(("isprime: quick result on %ld\n", (long)x));
491 return(0);
494 /* approximate square root of x */
495 for (stop = x; x/stop < stop; stop >>= 1)
496 continue;
497 stop <<= 1;
499 /* try odd numbers up to stop */
500 for (div = *--ip; div < stop; div += 2)
501 if (x%div == 0)
502 return(0);
504 return(1);
508 - dbzagain - set up a new database to be a rebuild of an old one
510 int /* 0 success, -1 failure */
511 dbzagain(name, oldname)
512 char *name; /* base name; .dir and .pag must exist */
513 char *oldname; /* base name; all must exist */
515 register char *fn;
516 struct dbzconfig c;
517 register int i;
518 register long top;
519 register FILE *f;
520 register int newtable;
521 register off_t newsize;
523 if (pagf != NULL) {
524 DEBUG(("dbzagain: database already open\n"));
525 return(-1);
528 /* pick up the old configuration */
529 fn = enstring(oldname, dir);
530 if (fn == NULL)
531 return(-1);
532 f = fopen(fn, "r");
533 free(fn);
534 if (f == NULL) {
535 DEBUG(("dbzagain: cannot open old .dir file\n"));
536 return(-1);
538 i = getconf(f, (FILE *)NULL, &c);
539 (void) fclose(f);
540 if (i < 0) {
541 DEBUG(("dbzagain: getconf failed\n"));
542 return(-1);
545 /* tinker with it */
546 top = 0;
547 newtable = 0;
548 for (i = 0; i < NUSEDS; i++) {
549 if (top < c.used[i])
550 top = c.used[i];
551 if (c.used[i] == 0)
552 newtable = 1; /* hasn't got full usage history yet */
554 if (top == 0) {
555 DEBUG(("dbzagain: old table has no contents!\n"));
556 newtable = 1;
558 for (i = NUSEDS-1; i > 0; i--)
559 c.used[i] = c.used[i-1];
560 c.used[0] = 0;
561 newsize = dbzsize(top);
562 if (!newtable || newsize > c.tsize) /* don't shrink new table */
563 c.tsize = newsize;
565 /* write it out */
566 fn = enstring(name, dir);
567 if (fn == NULL)
568 return(-1);
569 f = fopen(fn, "w");
570 free(fn);
571 if (f == NULL) {
572 DEBUG(("dbzagain: unable to write new .dir\n"));
573 return(-1);
575 i = putconf(f, &c);
576 (void) fclose(f);
577 if (i < 0) {
578 DEBUG(("dbzagain: putconf failed\n"));
579 return(-1);
582 /* create/truncate .pag */
583 fn = enstring(name, pag);
584 if (fn == NULL)
585 return(-1);
586 f = fopen(fn, "w");
587 free(fn);
588 if (f == NULL) {
589 DEBUG(("dbzagain: unable to create/truncate .pag file\n"));
590 return(-1);
591 } else
592 (void) fclose(f);
594 /* and let dbminit do the work */
595 return(dbminit(name));
599 - dbminit - open a database, creating it (using defaults) if necessary
601 * We try to leave errno set plausibly, to the extent that underlying
602 * functions permit this, since many people consult it if dbminit() fails.
604 int /* 0 success, -1 failure */
605 dbminit(name)
606 char *name;
608 register int i;
609 register size_t s;
610 register char *dirfname;
611 register char *pagfname;
613 if (pagf != NULL) {
614 DEBUG(("dbminit: dbminit already called once\n"));
615 errno = 0;
616 return(-1);
619 /* open the .dir file */
620 dirfname = enstring(name, dir);
621 if (dirfname == NULL)
622 return(-1);
623 dirf = fopen(dirfname, "r+");
624 if (dirf == NULL) {
625 dirf = fopen(dirfname, "r");
626 dirronly = 1;
627 } else
628 dirronly = 0;
629 free(dirfname);
630 if (dirf == NULL) {
631 DEBUG(("dbminit: can't open .dir file\n"));
632 return(-1);
635 /* open the .pag file */
636 pagfname = enstring(name, pag);
637 if (pagfname == NULL) {
638 (void) fclose(dirf);
639 return(-1);
641 pagf = fopen(pagfname, "r+b");
642 if (pagf == NULL) {
643 pagf = fopen(pagfname, "rb");
644 if (pagf == NULL) {
645 DEBUG(("dbminit: .pag open failed\n"));
646 (void) fclose(dirf);
647 free(pagfname);
648 return(-1);
650 pagronly = 1;
651 } else if (dirronly)
652 pagronly = 1;
653 else
654 pagronly = 0;
655 #ifdef NOBUFFER
657 * B News does not do adequate locking on its database accesses.
658 * Why it doesn't get into trouble using dbm is a mystery. In any
659 * case, doing unbuffered i/o does not cure the problem, but does
660 * enormously reduce its incidence.
662 (void) setbuf(pagf, (char *)NULL);
663 #else
664 #ifdef _IOFBF
665 (void) setvbuf(pagf, (char *)pagbuf, _IOFBF, sizeof(pagbuf));
666 #endif
667 #endif
668 pagpos = -1;
669 /* don't free pagfname, need it below */
671 /* open the base file */
672 basef = fopen(name, "r");
673 if (basef == NULL) {
674 DEBUG(("dbminit: basefile open failed\n"));
675 basefname = enstring(name, "");
676 if (basefname == NULL) {
677 (void) fclose(pagf);
678 (void) fclose(dirf);
679 free(pagfname);
680 pagf = NULL;
681 return(-1);
683 } else
684 basefname = NULL;
685 #ifdef _IOFBF
686 if (basef != NULL)
687 (void) setvbuf(basef, basebuf, _IOFBF, sizeof(basebuf));
688 #endif
690 /* pick up configuration */
691 if (getconf(dirf, pagf, &conf) < 0) {
692 DEBUG(("dbminit: getconf failure\n"));
693 (void) fclose(basef);
694 (void) fclose(pagf);
695 (void) fclose(dirf);
696 free(pagfname);
697 pagf = NULL;
698 errno = EDOM; /* kind of a kludge, but very portable */
699 return(-1);
701 tagbits = conf.tagmask << conf.tagshift;
702 taghere = conf.tagenb << conf.tagshift;
703 tagboth = tagbits | taghere;
704 mybytemap(mybmap);
705 bytesame = 1;
706 for (i = 0; i < SOF; i++)
707 if (mybmap[i] != conf.bytemap[i])
708 bytesame = 0;
710 /* get first table into core, if it looks desirable and feasible */
711 s = (size_t)conf.tsize * SOF;
712 if (incore && (off_t)(s/SOF) == conf.tsize) {
713 bufpagf = fopen(pagfname, (pagronly) ? "rb" : "r+b");
714 if (bufpagf != NULL)
715 corepag = getcore(bufpagf);
716 } else {
717 bufpagf = NULL;
718 corepag = NULL;
720 free(pagfname);
722 /* misc. setup */
723 crcinit();
724 written = 0;
725 prevp = FRESH;
726 DEBUG(("dbminit: succeeded\n"));
727 return(0);
731 - enstring - concatenate two strings into a malloced area
733 static char * /* NULL if malloc fails */
734 enstring(s1, s2)
735 char *s1;
736 char *s2;
738 register char *p;
740 p = malloc((size_t)strlen(s1) + (size_t)strlen(s2) + 1);
741 if (p != NULL) {
742 (void) strcpy(p, s1);
743 (void) strcat(p, s2);
744 } else {
745 DEBUG(("enstring(%s, %s) out of memory\n", s1, s2));
747 return(p);
751 - dbmclose - close a database
754 dbmclose()
756 register int ret = 0;
758 if (pagf == NULL) {
759 DEBUG(("dbmclose: not opened!\n"));
760 return(-1);
763 if (fclose(pagf) == EOF) {
764 DEBUG(("dbmclose: fclose(pagf) failed\n"));
765 ret = -1;
767 pagf = basef; /* ensure valid pointer; dbzsync checks it */
768 if (dbzsync() < 0)
769 ret = -1;
770 if (bufpagf != NULL && fclose(bufpagf) == EOF) {
771 DEBUG(("dbmclose: fclose(bufpagf) failed\n"));
772 ret = -1;
774 if (corepag != NULL)
775 free((char *)corepag);
776 corepag = NULL;
777 if (fclose(basef) == EOF) {
778 DEBUG(("dbmclose: fclose(basef) failed\n"));
779 ret = -1;
781 if (basefname != NULL)
782 free(basefname);
783 basef = NULL;
784 pagf = NULL;
785 if (fclose(dirf) == EOF) {
786 DEBUG(("dbmclose: fclose(dirf) failed\n"));
787 ret = -1;
790 DEBUG(("dbmclose: %s\n", (ret == 0) ? "succeeded" : "failed"));
791 return(ret);
795 - dbzsync - push all in-core data out to disk
798 dbzsync()
800 register int ret = 0;
802 if (pagf == NULL) {
803 DEBUG(("dbzsync: not opened!\n"));
804 return(-1);
806 if (!written)
807 return(0);
809 if (corepag != NULL) {
810 if (putcore(corepag, bufpagf) < 0) {
811 DEBUG(("dbzsync: putcore failed\n"));
812 ret = -1;
815 if (!conf.olddbz)
816 if (putconf(dirf, &conf) < 0)
817 ret = -1;
819 DEBUG(("dbzsync: %s\n", (ret == 0) ? "succeeded" : "failed"));
820 return(ret);
824 - dbzcancel - cancel writing of in-core data
825 * Mostly for use from child processes.
826 * Note that we don't need to futz around with stdio buffers, because we
827 * always fflush them immediately anyway and so they never have stale data.
830 dbzcancel()
832 if (pagf == NULL) {
833 DEBUG(("dbzcancel: not opened!\n"));
834 return(-1);
837 written = 0;
838 return(0);
842 - dbzfetch - fetch() with case mapping built in
844 datum
845 dbzfetch(key)
846 datum key;
848 char buffer[DBZMAXKEY + 1];
849 datum mappedkey;
850 register size_t keysize;
852 DEBUG(("dbzfetch: (%s)\n", key.dptr));
854 /* Key is supposed to be less than DBZMAXKEY */
855 keysize = key.dsize;
856 if (keysize >= DBZMAXKEY) {
857 keysize = DBZMAXKEY;
858 DEBUG(("keysize is %d - truncated to %d\n", key.dsize, DBZMAXKEY));
861 mappedkey.dptr = mapcase(buffer, key.dptr, keysize);
862 buffer[keysize] = '\0'; /* just a debug aid */
863 mappedkey.dsize = keysize;
865 return(fetch(mappedkey));
869 - fetch - get an entry from the database
871 * Disgusting fine point, in the name of backward compatibility: if the
872 * last character of "key" is a NUL, that character is (effectively) not
873 * part of the comparison against the stored keys.
875 datum /* dptr NULL, dsize 0 means failure */
876 fetch(key)
877 datum key;
879 char buffer[DBZMAXKEY + 1];
880 static off_t key_ptr; /* return value points here */
881 datum output;
882 register size_t keysize;
883 register size_t cmplen;
884 register char *sepp;
886 DEBUG(("fetch: (%s)\n", key.dptr));
887 output.dptr = NULL;
888 output.dsize = 0;
889 prevp = FRESH;
891 /* Key is supposed to be less than DBZMAXKEY */
892 keysize = key.dsize;
893 if (keysize >= DBZMAXKEY) {
894 keysize = DBZMAXKEY;
895 DEBUG(("keysize is %d - truncated to %d\n", key.dsize, DBZMAXKEY));
898 if (pagf == NULL) {
899 DEBUG(("fetch: database not open!\n"));
900 return(output);
901 } else if (basef == NULL) { /* basef didn't exist yet */
902 basef = latebase();
903 if (basef == NULL)
904 return(output);
907 cmplen = keysize;
908 sepp = &conf.fieldsep;
909 if (key.dptr[keysize-1] == '\0') {
910 cmplen--;
911 sepp = &buffer[keysize-1];
913 start(&srch, &key, FRESH);
914 while ((key_ptr = search(&srch)) != NOTFOUND) {
915 DEBUG(("got 0x%lx\n", key_ptr));
917 /* fetch the key */
918 if (fseek(basef, key_ptr, SEEK_SET) != 0) {
919 DEBUG(("fetch: seek failed\n"));
920 return(output);
922 if (fread(buffer, 1, keysize, basef) != keysize) {
923 DEBUG(("fetch: read failed\n"));
924 return(output);
927 /* try it */
928 buffer[keysize] = '\0'; /* terminated for DEBUG */
929 (void) mapcase(buffer, buffer, keysize);
930 DEBUG(("fetch: buffer (%s) looking for (%s) size = %d\n",
931 buffer, key.dptr, keysize));
932 if (memcmp(key.dptr, buffer, cmplen) == 0 &&
933 (*sepp == conf.fieldsep || *sepp == '\0')) {
934 /* we found it */
935 output.dptr = (char *)&key_ptr;
936 output.dsize = SOF;
937 DEBUG(("fetch: successful\n"));
938 return(output);
942 /* we didn't find it */
943 DEBUG(("fetch: failed\n"));
944 prevp = &srch; /* remember where we stopped */
945 return(output);
949 - latebase - try to open a base file that wasn't there at the start
951 static FILE *
952 latebase()
954 register FILE *it;
956 if (basefname == NULL) {
957 DEBUG(("latebase: name foulup\n"));
958 return(NULL);
960 it = fopen(basefname, "r");
961 if (it == NULL) {
962 DEBUG(("latebase: still can't open base\n"));
963 } else {
964 DEBUG(("latebase: late open succeeded\n"));
965 free(basefname);
966 basefname = NULL;
967 #ifdef _IOFBF
968 (void) setvbuf(it, basebuf, _IOFBF, sizeof(basebuf));
969 #endif
971 return(it);
975 - dbzstore - store() with case mapping built in
978 dbzstore(key, data)
979 datum key;
980 datum data;
982 char buffer[DBZMAXKEY + 1];
983 datum mappedkey;
984 register size_t keysize;
986 DEBUG(("dbzstore: (%s)\n", key.dptr));
988 /* Key is supposed to be less than DBZMAXKEY */
989 keysize = key.dsize;
990 if (keysize >= DBZMAXKEY) {
991 DEBUG(("dbzstore: key size too big (%d)\n", key.dsize));
992 return(-1);
995 mappedkey.dptr = mapcase(buffer, key.dptr, keysize);
996 buffer[keysize] = '\0'; /* just a debug aid */
997 mappedkey.dsize = keysize;
999 return(store(mappedkey, data));
1003 - store - add an entry to the database
1005 int /* 0 success, -1 failure */
1006 store(key, data)
1007 datum key;
1008 datum data;
1010 off_t value;
1012 if (pagf == NULL) {
1013 DEBUG(("store: database not open!\n"));
1014 return(-1);
1015 } else if (basef == NULL) { /* basef didn't exist yet */
1016 basef = latebase();
1017 if (basef == NULL)
1018 return(-1);
1020 if (pagronly) {
1021 DEBUG(("store: database open read-only\n"));
1022 return(-1);
1024 if (data.dsize != SOF) {
1025 DEBUG(("store: value size wrong (%d)\n", data.dsize));
1026 return(-1);
1028 if (key.dsize >= DBZMAXKEY) {
1029 DEBUG(("store: key size too big (%d)\n", key.dsize));
1030 return(-1);
1033 /* copy the value in to ensure alignment */
1034 (void) memcpy((char *)&value, data.dptr, SOF);
1035 DEBUG(("store: (%s, %ld)\n", key.dptr, (long)value));
1036 if (!okayvalue(value)) {
1037 DEBUG(("store: reserved bit or overflow in 0x%lx\n", value));
1038 return(-1);
1041 /* find the place, exploiting previous search if possible */
1042 start(&srch, &key, prevp);
1043 while (search(&srch) != NOTFOUND)
1044 continue;
1046 prevp = FRESH;
1047 conf.used[0]++;
1048 DEBUG(("store: used count %ld\n", conf.used[0]));
1049 written = 1;
1050 return(set(&srch, value));
1054 - dbzincore - control attempts to keep .pag file in core
1056 int /* old setting */
1057 dbzincore(value)
1058 int value;
1060 register int old = incore;
1062 incore = value;
1063 return(old);
1067 - getconf - get configuration from .dir file
1069 static int /* 0 success, -1 failure */
1070 getconf(df, pf, cp)
1071 register FILE *df; /* NULL means just give me the default */
1072 register FILE *pf; /* NULL means don't care about .pag */
1073 register struct dbzconfig *cp;
1075 register int c;
1076 register int i;
1077 int err = 0;
1079 c = (df != NULL) ? getc(df) : EOF;
1080 if (c == EOF) { /* empty file, no configuration known */
1081 cp->olddbz = 0;
1082 if (df != NULL && pf != NULL && getc(pf) != EOF)
1083 cp->olddbz = 1;
1084 cp->tsize = DEFSIZE;
1085 cp->fieldsep = '\t';
1086 for (i = 0; i < NUSEDS; i++)
1087 cp->used[i] = 0;
1088 cp->valuesize = SOF;
1089 mybytemap(cp->bytemap);
1090 cp->casemap = DEFCASE;
1091 cp->tagenb = TAGENB;
1092 cp->tagmask = TAGMASK;
1093 cp->tagshift = TAGSHIFT;
1094 DEBUG(("getconf: defaults (%ld, %c, (0x%lx/0x%lx<<%d))\n",
1095 cp->tsize, cp->casemap, cp->tagenb,
1096 cp->tagmask, cp->tagshift));
1097 return(0);
1099 (void) ungetc(c, df);
1101 /* first line, the vital stuff */
1102 if (getc(df) != 'd' || getc(df) != 'b' || getc(df) != 'z')
1103 err = -1;
1104 if (getno(df, &err) != dbzversion)
1105 err = -1;
1106 cp->tsize = getno(df, &err);
1107 cp->fieldsep = getno(df, &err);
1108 while ((c = getc(df)) == ' ')
1109 continue;
1110 cp->casemap = c;
1111 cp->tagenb = getno(df, &err);
1112 cp->tagmask = getno(df, &err);
1113 cp->tagshift = getno(df, &err);
1114 cp->valuesize = getno(df, &err);
1115 if (cp->valuesize != SOF) {
1116 DEBUG(("getconf: wrong off_t size (%d)\n", cp->valuesize));
1117 err = -1;
1118 cp->valuesize = SOF; /* to protect the loops below */
1120 for (i = 0; i < cp->valuesize; i++)
1121 cp->bytemap[i] = getno(df, &err);
1122 if (getc(df) != '\n')
1123 err = -1;
1124 DEBUG(("size %ld, sep %d, cmap %c, tags 0x%lx/0x%lx<<%d, ", cp->tsize,
1125 cp->fieldsep, cp->casemap, cp->tagenb, cp->tagmask,
1126 cp->tagshift));
1127 DEBUG(("bytemap (%d)", cp->valuesize));
1128 for (i = 0; i < cp->valuesize; i++) {
1129 DEBUG((" %d", cp->bytemap[i]));
1131 DEBUG(("\n"));
1133 /* second line, the usages */
1134 for (i = 0; i < NUSEDS; i++)
1135 cp->used[i] = getno(df, &err);
1136 if (getc(df) != '\n')
1137 err = -1;
1138 DEBUG(("used %ld %ld %ld...\n", cp->used[0], cp->used[1], cp->used[2]));
1140 if (err < 0) {
1141 DEBUG(("getconf error\n"));
1142 return(-1);
1144 return(0);
1148 - getno - get a long
1150 static long
1151 getno(f, ep)
1152 FILE *f;
1153 int *ep;
1155 register char *p;
1156 # define MAXN 50
1157 char getbuf[MAXN];
1158 register int c;
1160 while ((c = getc(f)) == ' ')
1161 continue;
1162 if (c == EOF || c == '\n') {
1163 DEBUG(("getno: missing number\n"));
1164 *ep = -1;
1165 return(0);
1167 p = getbuf;
1168 *p++ = c;
1169 while ((c = getc(f)) != EOF && c != '\n' && c != ' ')
1170 if (p < &getbuf[MAXN-1])
1171 *p++ = c;
1172 if (c == EOF) {
1173 DEBUG(("getno: EOF\n"));
1174 *ep = -1;
1175 } else
1176 (void) ungetc(c, f);
1177 *p = '\0';
1179 if (strspn(getbuf, "-1234567890") != strlen(getbuf)) {
1180 DEBUG(("getno: `%s' non-numeric\n", getbuf));
1181 *ep = -1;
1183 return(atol(getbuf));
1187 - putconf - write configuration to .dir file
1189 static int /* 0 success, -1 failure */
1190 putconf(f, cp)
1191 register FILE *f;
1192 register struct dbzconfig *cp;
1194 register int i;
1195 register int ret = 0;
1197 if (fseek(f, 0, SEEK_SET) != 0) {
1198 DEBUG(("fseek failure in putconf\n"));
1199 ret = -1;
1201 fprintf(f, "dbz %d %ld %d %c %ld %ld %d %d", dbzversion,
1202 (long)cp->tsize,
1203 cp->fieldsep, cp->casemap, (long)cp->tagenb,
1204 (long)cp->tagmask, cp->tagshift,
1205 cp->valuesize);
1207 for (i = 0; i < cp->valuesize; i++)
1208 fprintf(f, " %d", cp->bytemap[i]);
1209 fprintf(f, "\n");
1210 for (i = 0; i < NUSEDS; i++)
1211 fprintf(f, "%ld%c",
1212 (long)cp->used[i], (i < NUSEDS-1) ? ' ' : '\n');
1215 (void) fflush(f);
1216 if (ferror(f))
1217 ret = -1;
1219 DEBUG(("putconf status %d\n", ret));
1220 return(ret);
1224 - getcore - try to set up an in-core copy of .pag file
1226 static off_t * /* pointer to copy, or NULL */
1227 getcore(f)
1228 FILE *f;
1230 register off_t *p;
1231 register size_t i;
1232 register size_t nread;
1233 register char *it;
1235 it = malloc((size_t)conf.tsize * SOF);
1236 if (it == NULL) {
1237 DEBUG(("getcore: malloc failed\n"));
1238 return(NULL);
1241 nread = fread(it, SOF, (size_t)conf.tsize, f);
1242 if (ferror(f)) {
1243 DEBUG(("getcore: read failed\n"));
1244 free(it);
1245 return(NULL);
1248 p = (off_t *)it + nread;
1249 i = (size_t)conf.tsize - nread;
1250 while (i-- > 0)
1251 *p++ = VACANT;
1252 return((off_t *)it);
1256 - putcore - try to rewrite an in-core table
1258 static int /* 0 okay, -1 fail */
1259 putcore(tab, f)
1260 off_t *tab;
1261 FILE *f;
1263 if (fseek(f, 0, SEEK_SET) != 0) {
1264 DEBUG(("fseek failure in putcore\n"));
1265 return(-1);
1267 (void) fwrite((char *)tab, SOF, (size_t)conf.tsize, f);
1268 (void) fflush(f);
1269 return((ferror(f)) ? -1 : 0);
1273 - start - set up to start or restart a search
1275 static void
1276 start(sp, kp, osp)
1277 register struct searcher *sp;
1278 register datum *kp;
1279 register struct searcher *osp; /* may be FRESH, i.e. NULL */
1281 register long h;
1283 h = hash(kp->dptr, kp->dsize);
1284 if (osp != FRESH && osp->hash == h) {
1285 if (sp != osp)
1286 *sp = *osp;
1287 DEBUG(("search restarted\n"));
1288 } else {
1289 sp->hash = h;
1290 sp->tag = MKTAG(h / conf.tsize);
1291 DEBUG(("tag 0x%lx\n", sp->tag));
1292 sp->place = h % conf.tsize;
1293 sp->tabno = 0;
1294 sp->run = (conf.olddbz) ? conf.tsize : MAXRUN;
1295 sp->aborted = 0;
1297 sp->seen = 0;
1301 - search - conduct part of a search
1303 static off_t /* NOTFOUND if we hit VACANT or error */
1304 search(sp)
1305 register struct searcher *sp;
1307 register off_t dest;
1308 register off_t value;
1309 off_t val; /* buffer for value (can't fread register) */
1310 register off_t place;
1312 if (sp->aborted)
1313 return(NOTFOUND);
1315 for (;;) {
1316 /* determine location to be examined */
1317 place = sp->place;
1318 if (sp->seen) {
1319 /* go to next location */
1320 if (--sp->run <= 0) {
1321 sp->tabno++;
1322 sp->run = MAXRUN;
1324 place = (place+1)%conf.tsize + sp->tabno*conf.tsize;
1325 sp->place = place;
1326 } else
1327 sp->seen = 1; /* now looking at current location */
1328 DEBUG(("search @ %ld\n", place));
1330 /* get the tagged value */
1331 if (corepag != NULL && place < conf.tsize) {
1332 DEBUG(("search: in core\n"));
1333 value = MAPIN(corepag[place]);
1334 } else {
1335 /* seek, if necessary */
1336 dest = place * SOF;
1337 if (pagpos != dest) {
1338 if (fseek(pagf, dest, SEEK_SET) != 0) {
1339 DEBUG(("search: seek failed\n"));
1340 pagpos = -1;
1341 sp->aborted = 1;
1342 return(NOTFOUND);
1344 pagpos = dest;
1347 /* read it */
1348 if (fread((char *)&val, sizeof(val), 1, pagf) == 1)
1349 value = MAPIN(val);
1350 else if (ferror(pagf)) {
1351 DEBUG(("search: read failed\n"));
1352 pagpos = -1;
1353 sp->aborted = 1;
1354 return(NOTFOUND);
1355 } else
1356 value = VACANT;
1358 /* and finish up */
1359 pagpos += sizeof(val);
1362 /* vacant slot is always cause to return */
1363 if (value == VACANT) {
1364 DEBUG(("search: empty slot\n"));
1365 return(NOTFOUND);
1368 /* check the tag */
1369 value = UNBIAS(value);
1370 DEBUG(("got 0x%lx\n", value));
1371 if (!HASTAG(value)) {
1372 DEBUG(("tagless\n"));
1373 return(value);
1374 } else if (TAG(value) == sp->tag) {
1375 DEBUG(("match\n"));
1376 return(NOTAG(value));
1377 } else {
1378 DEBUG(("mismatch 0x%lx\n", TAG(value)));
1381 /* NOTREACHED */
1385 - okayvalue - check that a value can be stored
1387 static int /* predicate */
1388 okayvalue(value)
1389 off_t value;
1391 if (HASTAG(value))
1392 return(0);
1393 #ifdef OVERFLOW
1394 if (value == LONG_MAX) /* BIAS() and UNBIAS() will overflow */
1395 return(0);
1396 #endif
1397 return(1);
1401 - set - store a value into a location previously found by search
1403 static int /* 0 success, -1 failure */
1404 set(sp, value)
1405 register struct searcher *sp;
1406 off_t value;
1408 register off_t place = sp->place;
1409 register off_t v = value;
1411 if (sp->aborted)
1412 return(-1);
1414 if (CANTAG(v) && !conf.olddbz) {
1415 v |= sp->tag | taghere;
1416 if (v != UNBIAS(VACANT)) /* BIAS(v) won't look VACANT */
1417 #ifdef OVERFLOW
1418 if (v != LONG_MAX) /* and it won't overflow */
1419 #endif
1420 value = v;
1422 DEBUG(("tagged value is 0x%lx\n", value));
1423 value = BIAS(value);
1424 value = MAPOUT(value);
1426 /* If we have the index file in memory, use it */
1427 if (corepag != NULL && place < conf.tsize) {
1428 corepag[place] = value;
1429 DEBUG(("set: incore\n"));
1430 return(0);
1433 /* seek to spot */
1434 pagpos = -1; /* invalidate position memory */
1435 if (fseek(pagf, place * SOF, SEEK_SET) != 0) {
1436 DEBUG(("set: seek failed\n"));
1437 sp->aborted = 1;
1438 return(-1);
1441 /* write in data */
1442 if (fwrite((char *)&value, SOF, 1, pagf) != 1) {
1443 DEBUG(("set: write failed\n"));
1444 sp->aborted = 1;
1445 return(-1);
1447 /* fflush improves robustness, and buffer re-use is rare anyway */
1448 if (fflush(pagf) == EOF) {
1449 DEBUG(("set: fflush failed\n"));
1450 sp->aborted = 1;
1451 return(-1);
1454 DEBUG(("set: succeeded\n"));
1455 return(0);
1459 - mybytemap - determine this machine's byte map
1461 * A byte map is an array of ints, sizeof(off_t) of them. The 0th int
1462 * is the byte number of the high-order byte in my off_t, and so forth.
1464 static void
1465 mybytemap(map)
1466 int map[]; /* -> int[SOF] */
1468 union {
1469 off_t o;
1470 char c[SOF];
1471 } u;
1472 register int *mp = &map[SOF];
1473 register int ntodo;
1474 register int i;
1476 u.o = 1;
1477 for (ntodo = (int)SOF; ntodo > 0; ntodo--) {
1478 for (i = 0; i < SOF; i++)
1479 if (u.c[i] != 0)
1480 break;
1481 if (i == SOF) {
1482 /* trouble -- set it to *something* consistent */
1483 DEBUG(("mybytemap: nonexistent byte %d!!!\n", ntodo));
1484 for (i = 0; i < SOF; i++)
1485 map[i] = i;
1486 return;
1488 DEBUG(("mybytemap: byte %d\n", i));
1489 *--mp = i;
1490 while (u.c[i] != 0)
1491 u.o <<= 1;
1496 - bytemap - transform an off_t from byte ordering map1 to map2
1498 static off_t /* transformed result */
1499 bytemap(ino, map1, map2)
1500 off_t ino;
1501 int *map1;
1502 int *map2;
1504 union oc {
1505 off_t o;
1506 char c[SOF];
1508 union oc in;
1509 union oc out;
1510 register int i;
1512 in.o = ino;
1513 for (i = 0; i < SOF; i++)
1514 out.c[map2[i]] = in.c[map1[i]];
1515 return(out.o);
1519 * This is a simplified version of the pathalias hashing function.
1520 * Thanks to Steve Belovin and Peter Honeyman
1522 * hash a string into a long int. 31 bit crc (from andrew appel).
1523 * the crc table is computed at run time by crcinit() -- we could
1524 * precompute, but it takes 1 clock tick on a 750.
1526 * This fast table calculation works only if POLY is a prime polynomial
1527 * in the field of integers modulo 2. Since the coefficients of a
1528 * 32-bit polynomial won't fit in a 32-bit word, the high-order bit is
1529 * implicit. IT MUST ALSO BE THE CASE that the coefficients of orders
1530 * 31 down to 25 are zero. Happily, we have candidates, from
1531 * E. J. Watson, "Primitive Polynomials (Mod 2)", Math. Comp. 16 (1962):
1532 * x^32 + x^7 + x^5 + x^3 + x^2 + x^1 + x^0
1533 * x^31 + x^3 + x^0
1535 * We reverse the bits to get:
1536 * 111101010000000000000000000000001 but drop the last 1
1537 * f 5 0 0 0 0 0 0
1538 * 010010000000000000000000000000001 ditto, for 31-bit crc
1539 * 4 8 0 0 0 0 0 0
1542 #define POLY 0x48000000L /* 31-bit polynomial (avoids sign problems) */
1544 static long CrcTable[128];
1547 - crcinit - initialize tables for hash function
1549 static void
1550 crcinit()
1552 register int i, j;
1553 register long sum;
1555 for (i = 0; i < 128; ++i) {
1556 sum = 0L;
1557 for (j = 7 - 1; j >= 0; --j)
1558 if (i & (1 << j))
1559 sum ^= POLY >> j;
1560 CrcTable[i] = sum;
1562 DEBUG(("crcinit: done\n"));
1566 - hash - Honeyman's nice hashing function
1568 static long
1569 hash(name, size)
1570 register char *name;
1571 register int size;
1573 register long sum = 0L;
1575 while (size--) {
1576 sum = (sum >> 7) ^ CrcTable[(sum ^ (*name++)) & 0x7f];
1578 DEBUG(("hash: returns (%ld)\n", sum));
1579 return(sum);
1583 * case-mapping stuff
1585 * Borrowed from C News, by permission of the authors. Somewhat modified.
1587 * We exploit the fact that we are dealing only with headers here, and
1588 * headers are limited to the ASCII characters by RFC822. It is barely
1589 * possible that we might be dealing with a translation into another
1590 * character set, but in particular it's very unlikely for a header
1591 * character to be outside -128..255.
1593 * Life would be a whole lot simpler if tolower() could safely and portably
1594 * be applied to any char.
1597 #define OFFSET 128 /* avoid trouble with negative chars */
1599 /* must call casencmp before invoking TOLOW... */
1600 #define TOLOW(c) (cmap[(c)+OFFSET])
1602 /* ...but the use of it in CISTREQN is safe without the preliminary call (!) */
1603 /* CISTREQN is an optimised case-insensitive strncmp(a,b,n)==0; n > 0 */
1604 #define CISTREQN(a, b, n) \
1605 (TOLOW((a)[0]) == TOLOW((b)[0]) && casencmp(a, b, n) == 0)
1607 #define MAPSIZE (256+OFFSET)
1608 static char cmap[MAPSIZE]; /* relies on init to '\0' */
1609 static int mprimed = 0; /* has cmap been set up? */
1612 - mapprime - set up case-mapping stuff
1614 static void
1615 mapprime()
1617 register char *lp;
1618 register char *up;
1619 register int c;
1620 register int i;
1621 static char lower[] = "abcdefghijklmnopqrstuvwxyz";
1622 static char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1624 for (lp = lower, up = upper; *lp != '\0'; lp++, up++) {
1625 c = *lp;
1626 cmap[c+OFFSET] = c;
1627 cmap[*up+OFFSET] = c;
1629 for (i = 0; i < MAPSIZE; i++)
1630 if (cmap[i] == '\0')
1631 cmap[i] = (char)(i-OFFSET);
1632 mprimed = 1;
1636 - casencmp - case-independent strncmp
1638 static int /* < == > 0 */
1639 casencmp(s1, s2, len)
1640 char *s1;
1641 char *s2;
1642 int len;
1644 register char *p1;
1645 register char *p2;
1646 register int n;
1648 if (!mprimed)
1649 mapprime();
1651 p1 = s1;
1652 p2 = s2;
1653 n = len;
1654 while (--n >= 0 && *p1 != '\0' && TOLOW(*p1) == TOLOW(*p2)) {
1655 p1++;
1656 p2++;
1658 if (n < 0)
1659 return(0);
1662 * The following case analysis is necessary so that characters
1663 * which look negative collate low against normal characters but
1664 * high against the end-of-string NUL.
1666 if (*p1 == '\0' && *p2 == '\0')
1667 return(0);
1668 else if (*p1 == '\0')
1669 return(-1);
1670 else if (*p2 == '\0')
1671 return(1);
1672 else
1673 return(TOLOW(*p1) - TOLOW(*p2));
1677 - mapcase - do case-mapped copy
1679 static char * /* returns src or dst */
1680 mapcase(dst, src, siz)
1681 char *dst; /* destination, used only if mapping needed */
1682 char *src; /* source; src == dst is legal */
1683 size_t siz;
1685 register char *s;
1686 register char *d;
1687 register char *c; /* case break */
1688 register char *e; /* end of source */
1691 c = cipoint(src, siz);
1692 if (c == NULL)
1693 return(src);
1695 if (!mprimed)
1696 mapprime();
1697 s = src;
1698 e = s + siz;
1699 d = dst;
1701 while (s < c)
1702 *d++ = *s++;
1703 while (s < e)
1704 *d++ = TOLOW(*s++);
1706 return(dst);
1710 - cipoint - where in this message-ID does it become case-insensitive?
1712 * The RFC822 code is not quite complete. Absolute, total, full RFC822
1713 * compliance requires a horrible parsing job, because of the arcane
1714 * quoting conventions -- abc"def"ghi is not equivalent to abc"DEF"ghi,
1715 * for example. There are three or four things that might occur in the
1716 * domain part of a message-id that are case-sensitive. They don't seem
1717 * to ever occur in real news, thank Cthulhu. (What? You were expecting
1718 * a merciful and forgiving deity to be invoked in connection with RFC822?
1719 * Forget it; none of them would come near it.)
1721 static char * /* pointer into s, or NULL for "nowhere" */
1722 cipoint(s, siz)
1723 char *s;
1724 size_t siz;
1726 register char *p;
1727 static char post[] = "postmaster";
1728 static int plen = sizeof(post)-1;
1730 switch (conf.casemap) {
1731 case '0': /* unmapped, sensible */
1732 return(NULL);
1733 break;
1734 case 'C': /* C News, RFC 822 conformant (approx.) */
1735 p = memchr(s, '@', siz);
1736 if (p == NULL) /* no local/domain split */
1737 return(NULL); /* assume all local */
1738 else if (p - (s+1) == plen && CISTREQN(s+1, post, plen)) {
1739 /* crazy -- "postmaster" is case-insensitive */
1740 return(s);
1741 } else
1742 return(p);
1743 break;
1744 case '=': /* 2.11, neither sensible nor conformant */
1745 return(s); /* all case-insensitive */
1746 break;
1749 DEBUG(("cipoint: unknown case mapping `%c'\n", conf.casemap));
1750 return(NULL); /* just leave it alone */
1754 - dbzdebug - control dbz debugging at run time
1756 int /* old value */
1757 dbzdebug(value)
1758 int value;
1760 #ifdef DBZDEBUG
1761 register int old = debug;
1763 debug = value;
1764 return(old);
1765 #else
1766 return(-1);
1767 #endif