xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision e58c8952)
1 /*
2  * Copyright (c) 1992 Eric P. Allman.
3  * Copyright (c) 1992, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)map.c	8.25 (Berkeley) 04/17/94";
11 #endif /* not lint */
12 
13 #include "sendmail.h"
14 
15 #ifdef NDBM
16 #include <ndbm.h>
17 #endif
18 #ifdef NEWDB
19 #include <db.h>
20 #endif
21 #ifdef NIS
22 #include <rpcsvc/ypclnt.h>
23 #endif
24 
25 /*
26 **  MAP.C -- implementations for various map classes.
27 **
28 **	Each map class implements a series of functions:
29 **
30 **	bool map_parse(MAP *map, char *args)
31 **		Parse the arguments from the config file.  Return TRUE
32 **		if they were ok, FALSE otherwise.  Fill in map with the
33 **		values.
34 **
35 **	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
36 **		Look up the key in the given map.  If found, do any
37 **		rewriting the map wants (including "args" if desired)
38 **		and return the value.  Set *pstat to the appropriate status
39 **		on error and return NULL.  Args will be NULL if called
40 **		from the alias routines, although this should probably
41 **		not be relied upon.  It is suggested you call map_rewrite
42 **		to return the results -- it takes care of null termination
43 **		and uses a dynamically expanded buffer as needed.
44 **
45 **	void map_store(MAP *map, char *key, char *value)
46 **		Store the key:value pair in the map.
47 **
48 **	bool map_open(MAP *map, int mode)
49 **		Open the map for the indicated mode.  Mode should
50 **		be either O_RDONLY or O_RDWR.  Return TRUE if it
51 **		was opened successfully, FALSE otherwise.  If the open
52 **		failed an the MF_OPTIONAL flag is not set, it should
53 **		also print an error.  If the MF_ALIAS bit is set
54 **		and this map class understands the @:@ convention, it
55 **		should call aliaswait() before returning.
56 **
57 **	void map_close(MAP *map)
58 **		Close the map.
59 */
60 
61 #define DBMMODE		0644
62 
63 extern bool	aliaswait __P((MAP *, char *, int));
64 /*
65 **  MAP_PARSEARGS -- parse config line arguments for database lookup
66 **
67 **	This is a generic version of the map_parse method.
68 **
69 **	Parameters:
70 **		map -- the map being initialized.
71 **		ap -- a pointer to the args on the config line.
72 **
73 **	Returns:
74 **		TRUE -- if everything parsed OK.
75 **		FALSE -- otherwise.
76 **
77 **	Side Effects:
78 **		null terminates the filename; stores it in map
79 */
80 
81 bool
82 map_parseargs(map, ap)
83 	MAP *map;
84 	char *ap;
85 {
86 	register char *p = ap;
87 
88 	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
89 	for (;;)
90 	{
91 		while (isascii(*p) && isspace(*p))
92 			p++;
93 		if (*p != '-')
94 			break;
95 		switch (*++p)
96 		{
97 		  case 'N':
98 			map->map_mflags |= MF_INCLNULL;
99 			map->map_mflags &= ~MF_TRY0NULL;
100 			break;
101 
102 		  case 'O':
103 			map->map_mflags &= ~MF_TRY1NULL;
104 			break;
105 
106 		  case 'o':
107 			map->map_mflags |= MF_OPTIONAL;
108 			break;
109 
110 		  case 'f':
111 			map->map_mflags |= MF_NOFOLDCASE;
112 			break;
113 
114 		  case 'm':
115 			map->map_mflags |= MF_MATCHONLY;
116 			break;
117 
118 		  case 'a':
119 			map->map_app = ++p;
120 			break;
121 		}
122 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
123 			p++;
124 		if (*p != '\0')
125 			*p++ = '\0';
126 	}
127 	if (map->map_app != NULL)
128 		map->map_app = newstr(map->map_app);
129 
130 	if (*p != '\0')
131 	{
132 		map->map_file = p;
133 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
134 			p++;
135 		if (*p != '\0')
136 			*p++ = '\0';
137 		map->map_file = newstr(map->map_file);
138 	}
139 
140 	while (*p != '\0' && isascii(*p) && isspace(*p))
141 		p++;
142 	if (*p != '\0')
143 		map->map_rebuild = newstr(p);
144 
145 	if (map->map_file == NULL)
146 	{
147 		syserr("No file name for %s map %s",
148 			map->map_class->map_cname, map->map_mname);
149 		return FALSE;
150 	}
151 	return TRUE;
152 }
153 /*
154 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
155 **
156 **	It also adds the map_app string.  It can be used as a utility
157 **	in the map_lookup method.
158 **
159 **	Parameters:
160 **		map -- the map that causes this.
161 **		s -- the string to rewrite, NOT necessarily null terminated.
162 **		slen -- the length of s.
163 **		av -- arguments to interpolate into buf.
164 **
165 **	Returns:
166 **		Pointer to rewritten result.
167 **
168 **	Side Effects:
169 **		none.
170 */
171 
172 struct rwbuf
173 {
174 	int	rwb_len;	/* size of buffer */
175 	char	*rwb_buf;	/* ptr to buffer */
176 };
177 
178 struct rwbuf	RwBufs[2];	/* buffers for rewriting output */
179 
180 char *
181 map_rewrite(map, s, slen, av)
182 	register MAP *map;
183 	register char *s;
184 	int slen;
185 	char **av;
186 {
187 	register char *bp;
188 	register char c;
189 	char **avp;
190 	register char *ap;
191 	register struct rwbuf *rwb;
192 	int i;
193 	int len;
194 
195 	if (tTd(39, 1))
196 	{
197 		printf("map_rewrite(%.*s), av =", slen, s);
198 		if (av == NULL)
199 			printf(" (nullv)");
200 		else
201 		{
202 			for (avp = av; *avp != NULL; avp++)
203 				printf("\n\t%s", *avp);
204 		}
205 		printf("\n");
206 	}
207 
208 	rwb = RwBufs;
209 	if (av == NULL)
210 		rwb++;
211 
212 	/* count expected size of output (can safely overestimate) */
213 	i = len = slen;
214 	if (av != NULL)
215 	{
216 		bp = s;
217 		for (i = slen; --i >= 0 && (c = *bp++) != 0; )
218 		{
219 			if (c != '%')
220 				continue;
221 			if (--i < 0)
222 				break;
223 			c = *bp++;
224 			if (!(isascii(c) && isdigit(c)))
225 				continue;
226 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
227 				continue;
228 			if (*avp == NULL)
229 				continue;
230 			len += strlen(*avp);
231 		}
232 	}
233 	if (map->map_app != NULL)
234 		len += strlen(map->map_app);
235 	if (rwb->rwb_len < ++len)
236 	{
237 		/* need to malloc additional space */
238 		rwb->rwb_len = len;
239 		if (rwb->rwb_buf != NULL)
240 			free(rwb->rwb_buf);
241 		rwb->rwb_buf = xalloc(rwb->rwb_len);
242 	}
243 
244 	bp = rwb->rwb_buf;
245 	if (av == NULL)
246 	{
247 		bcopy(s, bp, slen);
248 		bp += slen;
249 	}
250 	else
251 	{
252 		while (--slen >= 0 && (c = *s++) != '\0')
253 		{
254 			if (c != '%')
255 			{
256   pushc:
257 				*bp++ = c;
258 				continue;
259 			}
260 			if (--slen < 0 || (c = *s++) == '\0')
261 				c = '%';
262 			if (c == '%')
263 				goto pushc;
264 			if (!(isascii(c) && isdigit(c)))
265 			{
266 				*bp++ = '%';
267 				goto pushc;
268 			}
269 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
270 				continue;
271 			if (*avp == NULL)
272 				continue;
273 
274 			/* transliterate argument into output string */
275 			for (ap = *avp; (c = *ap++) != '\0'; )
276 				*bp++ = c;
277 		}
278 	}
279 	if (map->map_app != NULL)
280 		strcpy(bp, map->map_app);
281 	else
282 		*bp = '\0';
283 	if (tTd(39, 1))
284 		printf("map_rewrite => %s\n", rwb->rwb_buf);
285 	return rwb->rwb_buf;
286 }
287 /*
288 **  INITMAPS -- initialize for aliasing
289 **
290 **	Parameters:
291 **		rebuild -- if TRUE, this rebuilds the cached versions.
292 **		e -- current envelope.
293 **
294 **	Returns:
295 **		none.
296 **
297 **	Side Effects:
298 **		initializes aliases:
299 **		if NDBM:  opens the database.
300 **		if ~NDBM: reads the aliases into the symbol table.
301 */
302 
303 initmaps(rebuild, e)
304 	bool rebuild;
305 	register ENVELOPE *e;
306 {
307 	extern void map_init();
308 
309 #ifdef XDEBUG
310 	checkfd012("entering initmaps");
311 #endif
312 	CurEnv = e;
313 	if (rebuild)
314 	{
315 		stabapply(map_init, 1);
316 		stabapply(map_init, 2);
317 	}
318 	else
319 	{
320 		stabapply(map_init, 0);
321 	}
322 #ifdef XDEBUG
323 	checkfd012("exiting initmaps");
324 #endif
325 }
326 
327 void
328 map_init(s, rebuild)
329 	register STAB *s;
330 	int rebuild;
331 {
332 	register MAP *map;
333 
334 	/* has to be a map */
335 	if (s->s_type != ST_MAP)
336 		return;
337 
338 	map = &s->s_map;
339 	if (!bitset(MF_VALID, map->map_mflags))
340 		return;
341 
342 	if (tTd(38, 2))
343 		printf("map_init(%s:%s, %d)\n",
344 			map->map_class->map_cname == NULL ? "NULL" :
345 				map->map_class->map_cname,
346 			map->map_file == NULL ? "NULL" : map->map_file,
347 			rebuild);
348 
349 	if (rebuild == (bitset(MF_ALIAS, map->map_mflags) &&
350 		    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) ? 1 : 2))
351 	{
352 		if (tTd(38, 3))
353 			printf("\twrong pass\n");
354 		return;
355 	}
356 
357 	/* if already open, close it (for nested open) */
358 	if (bitset(MF_OPEN, map->map_mflags))
359 	{
360 		map->map_class->map_close(map);
361 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
362 	}
363 
364 	if (rebuild == 2)
365 	{
366 		rebuildaliases(map, FALSE);
367 	}
368 	else
369 	{
370 		if (map->map_class->map_open(map, O_RDONLY))
371 		{
372 			if (tTd(38, 4))
373 				printf("\t%s:%s: valid\n",
374 					map->map_class->map_cname == NULL ? "NULL" :
375 						map->map_class->map_cname,
376 					map->map_file == NULL ? "NULL" :
377 						map->map_file);
378 			map->map_mflags |= MF_OPEN;
379 		}
380 		else if (tTd(38, 4))
381 			printf("\t%s:%s: invalid: %s\n",
382 				map->map_class->map_cname == NULL ? "NULL" :
383 					map->map_class->map_cname,
384 				map->map_file == NULL ? "NULL" :
385 					map->map_file,
386 				errstring(errno));
387 	}
388 }
389 /*
390 **  NDBM modules
391 */
392 
393 #ifdef NDBM
394 
395 /*
396 **  DBM_MAP_OPEN -- DBM-style map open
397 */
398 
399 bool
400 ndbm_map_open(map, mode)
401 	MAP *map;
402 	int mode;
403 {
404 	register DBM *dbm;
405 	struct stat st;
406 
407 	if (tTd(38, 2))
408 		printf("ndbm_map_open(%s, %d)\n", map->map_file, mode);
409 
410 	if (mode == O_RDWR)
411 		mode |= O_CREAT|O_TRUNC;
412 
413 	/* open the database */
414 	dbm = dbm_open(map->map_file, mode, DBMMODE);
415 	if (dbm == NULL)
416 	{
417 #ifdef MAYBENEXTRELEASE
418 		if (aliaswait(map, ".pag", FALSE))
419 			return TRUE;
420 #endif
421 		if (!bitset(MF_OPTIONAL, map->map_mflags))
422 			syserr("Cannot open DBM database %s", map->map_file);
423 		return FALSE;
424 	}
425 	map->map_db1 = (void *) dbm;
426 	if (mode == O_RDONLY)
427 	{
428 		if (bitset(MF_ALIAS, map->map_mflags) &&
429 		    !aliaswait(map, ".pag", TRUE))
430 			return FALSE;
431 	}
432 	else
433 	{
434 		int fd;
435 
436 		/* exclusive lock for duration of rebuild */
437 		fd = dbm_dirfno((DBM *) map->map_db1);
438 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags) &&
439 		    lockfile(fd, map->map_file, ".dir", LOCK_EX))
440 			map->map_mflags |= MF_LOCKED;
441 	}
442 	if (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0)
443 		map->map_mtime = st.st_mtime;
444 	return TRUE;
445 }
446 
447 
448 /*
449 **  DBM_MAP_LOOKUP -- look up a datum in a DBM-type map
450 */
451 
452 char *
453 ndbm_map_lookup(map, name, av, statp)
454 	MAP *map;
455 	char *name;
456 	char **av;
457 	int *statp;
458 {
459 	datum key, val;
460 	int fd;
461 	char keybuf[MAXNAME + 1];
462 
463 	if (tTd(38, 20))
464 		printf("ndbm_map_lookup(%s)\n", name);
465 
466 	key.dptr = name;
467 	key.dsize = strlen(name);
468 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
469 	{
470 		if (key.dsize > sizeof keybuf - 1)
471 			key.dsize = sizeof keybuf - 1;
472 		bcopy(key.dptr, keybuf, key.dsize + 1);
473 		makelower(keybuf);
474 		key.dptr = keybuf;
475 	}
476 	fd = dbm_dirfno((DBM *) map->map_db1);
477 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
478 		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
479 	val.dptr = NULL;
480 	if (bitset(MF_TRY0NULL, map->map_mflags))
481 	{
482 		val = dbm_fetch((DBM *) map->map_db1, key);
483 		if (val.dptr != NULL)
484 			map->map_mflags &= ~MF_TRY1NULL;
485 	}
486 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
487 	{
488 		key.dsize++;
489 		val = dbm_fetch((DBM *) map->map_db1, key);
490 		if (val.dptr != NULL)
491 			map->map_mflags &= ~MF_TRY0NULL;
492 	}
493 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
494 		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
495 	if (val.dptr == NULL)
496 		return NULL;
497 	if (bitset(MF_MATCHONLY, map->map_mflags))
498 		return map_rewrite(map, name, strlen(name), NULL);
499 	else
500 		return map_rewrite(map, val.dptr, val.dsize, av);
501 }
502 
503 
504 /*
505 **  DBM_MAP_STORE -- store a datum in the database
506 */
507 
508 void
509 ndbm_map_store(map, lhs, rhs)
510 	register MAP *map;
511 	char *lhs;
512 	char *rhs;
513 {
514 	datum key;
515 	datum data;
516 	int stat;
517 
518 	if (tTd(38, 12))
519 		printf("ndbm_map_store(%s, %s)\n", lhs, rhs);
520 
521 	key.dsize = strlen(lhs);
522 	key.dptr = lhs;
523 
524 	data.dsize = strlen(rhs);
525 	data.dptr = rhs;
526 
527 	if (bitset(MF_INCLNULL, map->map_mflags))
528 	{
529 		key.dsize++;
530 		data.dsize++;
531 	}
532 
533 	stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
534 	if (stat > 0)
535 	{
536 		usrerr("050 Warning: duplicate alias name %s", lhs);
537 		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
538 	}
539 	if (stat != 0)
540 		syserr("readaliases: dbm put (%s)", lhs);
541 }
542 
543 
544 /*
545 **  NDBM_MAP_CLOSE -- close the database
546 */
547 
548 void
549 ndbm_map_close(map)
550 	register MAP  *map;
551 {
552 	if (tTd(38, 9))
553 		printf("ndbm_map_close(%s, %x)\n", map->map_file, map->map_mflags);
554 
555 	if (bitset(MF_WRITABLE, map->map_mflags))
556 	{
557 #ifdef NIS
558 		bool inclnull;
559 		char buf[200];
560 
561 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
562 		map->map_mflags &= ~MF_INCLNULL;
563 
564 		(void) sprintf(buf, "%010ld", curtime());
565 		ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
566 
567 		(void) gethostname(buf, sizeof buf);
568 		ndbm_map_store(map, "YP_MASTER_NAME", buf);
569 
570 		if (inclnull)
571 			map->map_mflags |= MF_INCLNULL;
572 #endif
573 
574 		/* write out the distinguished alias */
575 		ndbm_map_store(map, "@", "@");
576 	}
577 	dbm_close((DBM *) map->map_db1);
578 }
579 
580 #endif
581 /*
582 **  NEWDB (Hash and BTree) Modules
583 */
584 
585 #ifdef NEWDB
586 
587 /*
588 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
589 **
590 **	These do rather bizarre locking.  If you can lock on open,
591 **	do that to avoid the condition of opening a database that
592 **	is being rebuilt.  If you don't, we'll try to fake it, but
593 **	there will be a race condition.  If opening for read-only,
594 **	we immediately release the lock to avoid freezing things up.
595 **	We really ought to hold the lock, but guarantee that we won't
596 **	be pokey about it.  That's hard to do.
597 */
598 
599 bool
600 bt_map_open(map, mode)
601 	MAP *map;
602 	int mode;
603 {
604 	DB *db;
605 	int i;
606 	int omode;
607 	int fd;
608 	struct stat st;
609 	char buf[MAXNAME];
610 
611 	if (tTd(38, 2))
612 		printf("bt_map_open(%s, %d)\n", map->map_file, mode);
613 
614 	omode = mode;
615 	if (omode == O_RDWR)
616 	{
617 		omode |= O_CREAT|O_TRUNC;
618 #if defined(O_EXLOCK) && HASFLOCK
619 		omode |= O_EXLOCK;
620 # if !OLD_NEWDB
621 	}
622 	else
623 	{
624 		omode |= O_SHLOCK;
625 # endif
626 #endif
627 	}
628 
629 	(void) strcpy(buf, map->map_file);
630 	i = strlen(buf);
631 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
632 		(void) strcat(buf, ".db");
633 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
634 	if (db == NULL)
635 	{
636 #ifdef MAYBENEXTRELEASE
637 		if (aliaswait(map, ".db", FALSE))
638 			return TRUE;
639 #endif
640 		if (!bitset(MF_OPTIONAL, map->map_mflags))
641 			syserr("Cannot open BTREE database %s", map->map_file);
642 		return FALSE;
643 	}
644 #if !OLD_NEWDB && HASFLOCK
645 	fd = db->fd(db);
646 # if !defined(O_EXLOCK)
647 	if (mode == O_RDWR && fd >= 0)
648 	{
649 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
650 			map->map_mflags |= MF_LOCKED;
651 	}
652 # else
653 	if (mode == O_RDONLY && fd >= 0)
654 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
655 	else
656 		map->map_mflags |= MF_LOCKED;
657 # endif
658 #endif
659 
660 	/* try to make sure that at least the database header is on disk */
661 	if (mode == O_RDWR)
662 #if OLD_NEWDB
663 		(void) db->sync(db);
664 #else
665 		(void) db->sync(db, 0);
666 
667 	if (fd >= 0 && fstat(fd, &st) >= 0)
668 		map->map_mtime = st.st_mtime;
669 #endif
670 
671 	map->map_db2 = (void *) db;
672 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
673 		if (!aliaswait(map, ".db", TRUE))
674 			return FALSE;
675 	return TRUE;
676 }
677 
678 
679 /*
680 **  HASH_MAP_INIT -- HASH-style map initialization
681 */
682 
683 bool
684 hash_map_open(map, mode)
685 	MAP *map;
686 	int mode;
687 {
688 	DB *db;
689 	int i;
690 	int omode;
691 	int fd;
692 	struct stat st;
693 	char buf[MAXNAME];
694 
695 	if (tTd(38, 2))
696 		printf("hash_map_open(%s, %d)\n", map->map_file, mode);
697 
698 	omode = mode;
699 	if (omode == O_RDWR)
700 	{
701 		omode |= O_CREAT|O_TRUNC;
702 #if defined(O_EXLOCK) && HASFLOCK
703 		omode |= O_EXLOCK;
704 # if !OLD_NEWDB
705 	}
706 	else
707 	{
708 		omode |= O_SHLOCK;
709 # endif
710 #endif
711 	}
712 
713 	(void) strcpy(buf, map->map_file);
714 	i = strlen(buf);
715 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
716 		(void) strcat(buf, ".db");
717 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
718 	if (db == NULL)
719 	{
720 #ifdef MAYBENEXTRELEASE
721 		if (aliaswait(map, ".db", FALSE))
722 			return TRUE;
723 #endif
724 		if (!bitset(MF_OPTIONAL, map->map_mflags))
725 			syserr("Cannot open HASH database %s", map->map_file);
726 		return FALSE;
727 	}
728 #if !OLD_NEWDB && HASFLOCK
729 	fd = db->fd(db);
730 # if !defined(O_EXLOCK)
731 	if (mode == O_RDWR && fd >= 0)
732 	{
733 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
734 			map->map_mflags |= MF_LOCKED;
735 	}
736 # else
737 	if (mode == O_RDONLY && fd >= 0)
738 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
739 	else
740 		map->map_mflags |= MF_LOCKED;
741 # endif
742 #endif
743 
744 	/* try to make sure that at least the database header is on disk */
745 	if (mode == O_RDWR)
746 #if OLD_NEWDB
747 		(void) db->sync(db);
748 #else
749 		(void) db->sync(db, 0);
750 
751 	if (fd >= 0 && fstat(fd, &st) >= 0)
752 		map->map_mtime = st.st_mtime;
753 #endif
754 
755 	map->map_db2 = (void *) db;
756 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
757 		if (!aliaswait(map, ".db", TRUE))
758 			return FALSE;
759 	return TRUE;
760 }
761 
762 
763 /*
764 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
765 */
766 
767 char *
768 db_map_lookup(map, name, av, statp)
769 	MAP *map;
770 	char *name;
771 	char **av;
772 	int *statp;
773 {
774 	DBT key, val;
775 	register DB *db = (DB *) map->map_db2;
776 	int st;
777 	int saveerrno;
778 	int fd;
779 	char keybuf[MAXNAME + 1];
780 
781 	if (tTd(38, 20))
782 		printf("db_map_lookup(%s)\n", name);
783 
784 	key.size = strlen(name);
785 	if (key.size > sizeof keybuf - 1)
786 		key.size = sizeof keybuf - 1;
787 	key.data = keybuf;
788 	bcopy(name, keybuf, key.size + 1);
789 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
790 		makelower(keybuf);
791 #if !OLD_NEWDB
792 	fd = db->fd(db);
793 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
794 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
795 #endif
796 	st = 1;
797 	if (bitset(MF_TRY0NULL, map->map_mflags))
798 	{
799 		st = db->get(db, &key, &val, 0);
800 		if (st == 0)
801 			map->map_mflags &= ~MF_TRY1NULL;
802 	}
803 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
804 	{
805 		key.size++;
806 		st = db->get(db, &key, &val, 0);
807 		if (st == 0)
808 			map->map_mflags &= ~MF_TRY0NULL;
809 	}
810 	saveerrno = errno;
811 #if !OLD_NEWDB
812 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
813 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
814 #endif
815 	if (st != 0)
816 	{
817 		errno = saveerrno;
818 		if (st < 0)
819 			syserr("db_map_lookup: get (%s)", name);
820 		return NULL;
821 	}
822 	if (bitset(MF_MATCHONLY, map->map_mflags))
823 		return map_rewrite(map, name, strlen(name), NULL);
824 	else
825 		return map_rewrite(map, val.data, val.size, av);
826 }
827 
828 
829 /*
830 **  DB_MAP_STORE -- store a datum in the NEWDB database
831 */
832 
833 void
834 db_map_store(map, lhs, rhs)
835 	register MAP *map;
836 	char *lhs;
837 	char *rhs;
838 {
839 	int stat;
840 	DBT key;
841 	DBT data;
842 	register DB *db = map->map_db2;
843 
844 	if (tTd(38, 20))
845 		printf("db_map_store(%s, %s)\n", lhs, rhs);
846 
847 	key.size = strlen(lhs);
848 	key.data = lhs;
849 
850 	data.size = strlen(rhs);
851 	data.data = rhs;
852 
853 	if (bitset(MF_INCLNULL, map->map_mflags))
854 	{
855 		key.size++;
856 		data.size++;
857 	}
858 
859 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
860 	if (stat > 0)
861 	{
862 		usrerr("050 Warning: duplicate alias name %s", lhs);
863 		stat = db->put(db, &key, &data, 0);
864 	}
865 	if (stat != 0)
866 		syserr("readaliases: db put (%s)", lhs);
867 }
868 
869 
870 /*
871 **  DB_MAP_CLOSE -- add distinguished entries and close the database
872 */
873 
874 void
875 db_map_close(map)
876 	MAP *map;
877 {
878 	register DB *db = map->map_db2;
879 
880 	if (tTd(38, 9))
881 		printf("db_map_close(%s, %x)\n", map->map_file, map->map_mflags);
882 
883 	if (bitset(MF_WRITABLE, map->map_mflags))
884 	{
885 		/* write out the distinguished alias */
886 		db_map_store(map, "@", "@");
887 	}
888 
889 	if (db->close(db) != 0)
890 		syserr("readaliases: db close failure");
891 }
892 
893 #endif
894 /*
895 **  NIS Modules
896 */
897 
898 # ifdef NIS
899 
900 # ifndef YPERR_BUSY
901 #  define YPERR_BUSY	16
902 # endif
903 
904 /*
905 **  NIS_MAP_OPEN -- open DBM map
906 */
907 
908 bool
909 nis_map_open(map, mode)
910 	MAP *map;
911 	int mode;
912 {
913 	int yperr;
914 	register char *p;
915 	auto char *vp;
916 	auto int vsize;
917 	char *master;
918 
919 	if (tTd(38, 2))
920 		printf("nis_map_open(%s)\n", map->map_file);
921 
922 	if (mode != O_RDONLY)
923 	{
924 		/* issue a pseudo-error message */
925 #ifdef ENOSYS
926 		errno = ENOSYS;
927 #else
928 # ifdef EFTYPE
929 		errno = EFTYPE;
930 # else
931 		errno = ENXIO;
932 # endif
933 #endif
934 		return FALSE;
935 	}
936 
937 	p = strchr(map->map_file, '@');
938 	if (p != NULL)
939 	{
940 		*p++ = '\0';
941 		if (*p != '\0')
942 			map->map_domain = p;
943 	}
944 
945 	if (*map->map_file == '\0')
946 		map->map_file = "mail.aliases";
947 
948 	if (map->map_domain == NULL)
949 	{
950 		yperr = yp_get_default_domain(&map->map_domain);
951 		if (yperr != 0)
952 		{
953 			if (!bitset(MF_OPTIONAL, map->map_mflags))
954 				syserr("NIS map %s specified, but NIS not running\n",
955 					map->map_file);
956 			return FALSE;
957 		}
958 	}
959 
960 	/* check to see if this map actually exists */
961 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
962 			&vp, &vsize);
963 	if (tTd(38, 10))
964 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
965 			map->map_domain, map->map_file, yperr_string(yperr));
966 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
967 		return TRUE;
968 
969 	if (!bitset(MF_OPTIONAL, map->map_mflags))
970 		syserr("Cannot bind to domain %s: %s", map->map_domain,
971 			yperr_string(yperr));
972 
973 	return FALSE;
974 }
975 
976 
977 /*
978 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
979 */
980 
981 char *
982 nis_map_lookup(map, name, av, statp)
983 	MAP *map;
984 	char *name;
985 	char **av;
986 	int *statp;
987 {
988 	char *vp;
989 	auto int vsize;
990 	int buflen;
991 	int yperr;
992 	char keybuf[MAXNAME + 1];
993 
994 	if (tTd(38, 20))
995 		printf("nis_map_lookup(%s)\n", name);
996 
997 	buflen = strlen(name);
998 	if (buflen > sizeof keybuf - 1)
999 		buflen = sizeof keybuf - 1;
1000 	bcopy(name, keybuf, buflen + 1);
1001 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1002 		makelower(keybuf);
1003 	yperr = YPERR_KEY;
1004 	if (bitset(MF_TRY0NULL, map->map_mflags))
1005 	{
1006 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1007 			     &vp, &vsize);
1008 		if (yperr == 0)
1009 			map->map_mflags &= ~MF_TRY1NULL;
1010 	}
1011 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1012 	{
1013 		buflen++;
1014 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1015 			     &vp, &vsize);
1016 		if (yperr == 0)
1017 			map->map_mflags &= ~MF_TRY0NULL;
1018 	}
1019 	if (yperr != 0)
1020 	{
1021 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1022 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1023 		return NULL;
1024 	}
1025 	if (bitset(MF_MATCHONLY, map->map_mflags))
1026 		return map_rewrite(map, name, strlen(name), NULL);
1027 	else
1028 		return map_rewrite(map, vp, vsize, av);
1029 }
1030 
1031 
1032 /*
1033 **  NIS_MAP_STORE
1034 */
1035 
1036 void
1037 nis_map_store(map, lhs, rhs)
1038 	MAP *map;
1039 	char *lhs;
1040 	char *rhs;
1041 {
1042 	/* nothing */
1043 }
1044 
1045 
1046 /*
1047 **  NIS_MAP_CLOSE
1048 */
1049 
1050 void
1051 nis_map_close(map)
1052 	MAP *map;
1053 {
1054 	/* nothing */
1055 }
1056 
1057 #endif /* NIS */
1058 /*
1059 **  STAB (Symbol Table) Modules
1060 */
1061 
1062 
1063 /*
1064 **  STAB_MAP_LOOKUP -- look up alias in symbol table
1065 */
1066 
1067 char *
1068 stab_map_lookup(map, name, av, pstat)
1069 	register MAP *map;
1070 	char *name;
1071 	char **av;
1072 	int *pstat;
1073 {
1074 	register STAB *s;
1075 
1076 	if (tTd(38, 20))
1077 		printf("stab_lookup(%s)\n", name);
1078 
1079 	s = stab(name, ST_ALIAS, ST_FIND);
1080 	if (s != NULL)
1081 		return (s->s_alias);
1082 	return (NULL);
1083 }
1084 
1085 
1086 /*
1087 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1088 */
1089 
1090 void
1091 stab_map_store(map, lhs, rhs)
1092 	register MAP *map;
1093 	char *lhs;
1094 	char *rhs;
1095 {
1096 	register STAB *s;
1097 
1098 	s = stab(lhs, ST_ALIAS, ST_ENTER);
1099 	s->s_alias = newstr(rhs);
1100 }
1101 
1102 
1103 /*
1104 **  STAB_MAP_OPEN -- initialize (reads data file)
1105 **
1106 **	This is a wierd case -- it is only intended as a fallback for
1107 **	aliases.  For this reason, opens for write (only during a
1108 **	"newaliases") always fails, and opens for read open the
1109 **	actual underlying text file instead of the database.
1110 */
1111 
1112 bool
1113 stab_map_open(map, mode)
1114 	register MAP *map;
1115 	int mode;
1116 {
1117 	FILE *af;
1118 	struct stat st;
1119 
1120 	if (tTd(38, 2))
1121 		printf("stab_map_open(%s)\n", map->map_file);
1122 
1123 	if (mode != O_RDONLY)
1124 	{
1125 		errno = ENODEV;
1126 		return FALSE;
1127 	}
1128 
1129 	af = fopen(map->map_file, "r");
1130 	if (af == NULL)
1131 		return FALSE;
1132 	readaliases(map, af, TRUE);
1133 
1134 	if (fstat(fileno(af), &st) >= 0)
1135 		map->map_mtime = st.st_mtime;
1136 	fclose(af);
1137 
1138 	return TRUE;
1139 }
1140 
1141 
1142 /*
1143 **  STAB_MAP_CLOSE -- close symbol table.
1144 **
1145 **	Since this is in memory, there is nothing to do.
1146 */
1147 
1148 void
1149 stab_map_close(map)
1150 	MAP *map;
1151 {
1152 	/* ignore it */
1153 }
1154 /*
1155 **  Implicit Modules
1156 **
1157 **	Tries several types.  For back compatibility of aliases.
1158 */
1159 
1160 
1161 /*
1162 **  IMPL_MAP_LOOKUP -- lookup in best open database
1163 */
1164 
1165 char *
1166 impl_map_lookup(map, name, av, pstat)
1167 	MAP *map;
1168 	char *name;
1169 	char **av;
1170 	int *pstat;
1171 {
1172 	if (tTd(38, 20))
1173 		printf("impl_map_lookup(%s)\n", name);
1174 
1175 #ifdef NEWDB
1176 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1177 		return db_map_lookup(map, name, av, pstat);
1178 #endif
1179 #ifdef NDBM
1180 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1181 		return ndbm_map_lookup(map, name, av, pstat);
1182 #endif
1183 	return stab_map_lookup(map, name, av, pstat);
1184 }
1185 
1186 /*
1187 **  IMPL_MAP_STORE -- store in open databases
1188 */
1189 
1190 void
1191 impl_map_store(map, lhs, rhs)
1192 	MAP *map;
1193 	char *lhs;
1194 	char *rhs;
1195 {
1196 #ifdef NEWDB
1197 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1198 		db_map_store(map, lhs, rhs);
1199 #endif
1200 #ifdef NDBM
1201 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1202 		ndbm_map_store(map, lhs, rhs);
1203 #endif
1204 	stab_map_store(map, lhs, rhs);
1205 }
1206 
1207 /*
1208 **  IMPL_MAP_OPEN -- implicit database open
1209 */
1210 
1211 bool
1212 impl_map_open(map, mode)
1213 	MAP *map;
1214 	int mode;
1215 {
1216 	struct stat stb;
1217 
1218 	if (tTd(38, 2))
1219 		printf("impl_map_open(%s, %d)\n", map->map_file, mode);
1220 
1221 	if (stat(map->map_file, &stb) < 0)
1222 	{
1223 		/* no alias file at all */
1224 		if (tTd(38, 3))
1225 			printf("no map file\n");
1226 		return FALSE;
1227 	}
1228 
1229 #ifdef NEWDB
1230 	map->map_mflags |= MF_IMPL_HASH;
1231 	if (hash_map_open(map, mode))
1232 	{
1233 #if defined(NDBM) && defined(NIS)
1234 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
1235 #endif
1236 			return TRUE;
1237 	}
1238 	else
1239 		map->map_mflags &= ~MF_IMPL_HASH;
1240 #endif
1241 #ifdef NDBM
1242 	map->map_mflags |= MF_IMPL_NDBM;
1243 	if (ndbm_map_open(map, mode))
1244 	{
1245 		return TRUE;
1246 	}
1247 	else
1248 		map->map_mflags &= ~MF_IMPL_NDBM;
1249 #endif
1250 
1251 #if defined(NEWDB) || defined(NDBM)
1252 	if (Verbose)
1253 		message("WARNING: cannot open alias database %s", map->map_file);
1254 #else
1255 	if (mode != O_RDONLY)
1256 		usrerr("Cannot rebuild aliases: no database format defined");
1257 #endif
1258 
1259 	return stab_map_open(map, mode);
1260 }
1261 
1262 
1263 /*
1264 **  IMPL_MAP_CLOSE -- close any open database(s)
1265 */
1266 
1267 void
1268 impl_map_close(map)
1269 	MAP *map;
1270 {
1271 #ifdef NEWDB
1272 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1273 	{
1274 		db_map_close(map);
1275 		map->map_mflags &= ~MF_IMPL_HASH;
1276 	}
1277 #endif
1278 
1279 #ifdef NDBM
1280 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1281 	{
1282 		ndbm_map_close(map);
1283 		map->map_mflags &= ~MF_IMPL_NDBM;
1284 	}
1285 #endif
1286 }
1287 /*
1288 **  NULL stubs
1289 */
1290 
1291 bool
1292 null_map_open(map, mode)
1293 	MAP *map;
1294 	int mode;
1295 {
1296 	return TRUE;
1297 }
1298 
1299 void
1300 null_map_close(map)
1301 	MAP *map;
1302 {
1303 	return;
1304 }
1305 
1306 void
1307 null_map_store(map, key, val)
1308 	MAP *map;
1309 	char *key;
1310 	char *val;
1311 {
1312 	return;
1313 }
1314