xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision 7e21a27b)
1 /*
2  * Copyright (c) 1992, 1995 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.57 (Berkeley) 04/24/95";
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   struct dom_binding { int dummy; };	/* needed on IRIX */
23 # include <rpcsvc/ypclnt.h>
24 #endif
25 
26 /*
27 **  MAP.C -- implementations for various map classes.
28 **
29 **	Each map class implements a series of functions:
30 **
31 **	bool map_parse(MAP *map, char *args)
32 **		Parse the arguments from the config file.  Return TRUE
33 **		if they were ok, FALSE otherwise.  Fill in map with the
34 **		values.
35 **
36 **	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
37 **		Look up the key in the given map.  If found, do any
38 **		rewriting the map wants (including "args" if desired)
39 **		and return the value.  Set *pstat to the appropriate status
40 **		on error and return NULL.  Args will be NULL if called
41 **		from the alias routines, although this should probably
42 **		not be relied upon.  It is suggested you call map_rewrite
43 **		to return the results -- it takes care of null termination
44 **		and uses a dynamically expanded buffer as needed.
45 **
46 **	void map_store(MAP *map, char *key, char *value)
47 **		Store the key:value pair in the map.
48 **
49 **	bool map_open(MAP *map, int mode)
50 **		Open the map for the indicated mode.  Mode should
51 **		be either O_RDONLY or O_RDWR.  Return TRUE if it
52 **		was opened successfully, FALSE otherwise.  If the open
53 **		failed an the MF_OPTIONAL flag is not set, it should
54 **		also print an error.  If the MF_ALIAS bit is set
55 **		and this map class understands the @:@ convention, it
56 **		should call aliaswait() before returning.
57 **
58 **	void map_close(MAP *map)
59 **		Close the map.
60 */
61 
62 #define DBMMODE		0644
63 
64 extern bool	aliaswait __P((MAP *, char *, int));
65 /*
66 **  MAP_PARSEARGS -- parse config line arguments for database lookup
67 **
68 **	This is a generic version of the map_parse method.
69 **
70 **	Parameters:
71 **		map -- the map being initialized.
72 **		ap -- a pointer to the args on the config line.
73 **
74 **	Returns:
75 **		TRUE -- if everything parsed OK.
76 **		FALSE -- otherwise.
77 **
78 **	Side Effects:
79 **		null terminates the filename; stores it in map
80 */
81 
82 bool
83 map_parseargs(map, ap)
84 	MAP *map;
85 	char *ap;
86 {
87 	register char *p = ap;
88 
89 	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
90 	for (;;)
91 	{
92 		while (isascii(*p) && isspace(*p))
93 			p++;
94 		if (*p != '-')
95 			break;
96 		switch (*++p)
97 		{
98 		  case 'N':
99 			map->map_mflags |= MF_INCLNULL;
100 			map->map_mflags &= ~MF_TRY0NULL;
101 			break;
102 
103 		  case 'O':
104 			map->map_mflags &= ~MF_TRY1NULL;
105 			break;
106 
107 		  case 'o':
108 			map->map_mflags |= MF_OPTIONAL;
109 			break;
110 
111 		  case 'f':
112 			map->map_mflags |= MF_NOFOLDCASE;
113 			break;
114 
115 		  case 'm':
116 			map->map_mflags |= MF_MATCHONLY;
117 			break;
118 
119 		  case 'A':
120 			map->map_mflags |= MF_APPEND;
121 			break;
122 
123 		  case 'a':
124 			map->map_app = ++p;
125 			break;
126 
127 		  case 'k':
128 			while (isascii(*++p) && isspace(*p))
129 				continue;
130 			map->map_keycolnm = p;
131 			break;
132 
133 		  case 'v':
134 			while (isascii(*++p) && isspace(*p))
135 				continue;
136 			map->map_valcolnm = p;
137 			break;
138 
139 		  case 'z':
140 			if (*++p != '\\')
141 				map->map_coldelim = *p;
142 			else
143 			{
144 				switch (*++p)
145 				{
146 				  case 'n':
147 					map->map_coldelim = '\n';
148 					break;
149 
150 				  case 't':
151 					map->map_coldelim = '\t';
152 					break;
153 
154 				  default:
155 					map->map_coldelim = '\\';
156 				}
157 			}
158 			break;
159 #ifdef RESERVED_FOR_SUN
160 		  case 'd':
161 			map->map_mflags |= MF_DOMAIN_WIDE;
162 			break;
163 
164 		  case 's':
165 			/* info type */
166 			break;
167 #endif
168 		}
169 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
170 			p++;
171 		if (*p != '\0')
172 			*p++ = '\0';
173 	}
174 	if (map->map_app != NULL)
175 		map->map_app = newstr(map->map_app);
176 	if (map->map_keycolnm != NULL)
177 		map->map_keycolnm = newstr(map->map_keycolnm);
178 	if (map->map_valcolnm != NULL)
179 		map->map_valcolnm = newstr(map->map_valcolnm);
180 
181 	if (*p != '\0')
182 	{
183 		map->map_file = p;
184 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
185 			p++;
186 		if (*p != '\0')
187 			*p++ = '\0';
188 		map->map_file = newstr(map->map_file);
189 	}
190 
191 	while (*p != '\0' && isascii(*p) && isspace(*p))
192 		p++;
193 	if (*p != '\0')
194 		map->map_rebuild = newstr(p);
195 
196 	if (map->map_file == NULL &&
197 	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
198 	{
199 		syserr("No file name for %s map %s",
200 			map->map_class->map_cname, map->map_mname);
201 		return FALSE;
202 	}
203 	return TRUE;
204 }
205 /*
206 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
207 **
208 **	It also adds the map_app string.  It can be used as a utility
209 **	in the map_lookup method.
210 **
211 **	Parameters:
212 **		map -- the map that causes this.
213 **		s -- the string to rewrite, NOT necessarily null terminated.
214 **		slen -- the length of s.
215 **		av -- arguments to interpolate into buf.
216 **
217 **	Returns:
218 **		Pointer to rewritten result.  This is static data that
219 **		should be copied if it is to be saved!
220 **
221 **	Side Effects:
222 **		none.
223 */
224 
225 char *
226 map_rewrite(map, s, slen, av)
227 	register MAP *map;
228 	register char *s;
229 	int slen;
230 	char **av;
231 {
232 	register char *bp;
233 	register char c;
234 	char **avp;
235 	register char *ap;
236 	int i;
237 	int len;
238 	static int buflen = -1;
239 	static char *buf = NULL;
240 
241 	if (tTd(39, 1))
242 	{
243 		printf("map_rewrite(%.*s), av =", slen, s);
244 		if (av == NULL)
245 			printf(" (nullv)");
246 		else
247 		{
248 			for (avp = av; *avp != NULL; avp++)
249 				printf("\n\t%s", *avp);
250 		}
251 		printf("\n");
252 	}
253 
254 	/* count expected size of output (can safely overestimate) */
255 	i = len = slen;
256 	if (av != NULL)
257 	{
258 		bp = s;
259 		for (i = slen; --i >= 0 && (c = *bp++) != 0; )
260 		{
261 			if (c != '%')
262 				continue;
263 			if (--i < 0)
264 				break;
265 			c = *bp++;
266 			if (!(isascii(c) && isdigit(c)))
267 				continue;
268 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
269 				continue;
270 			if (*avp == NULL)
271 				continue;
272 			len += strlen(*avp);
273 		}
274 	}
275 	if (map->map_app != NULL)
276 		len += strlen(map->map_app);
277 	if (buflen < ++len)
278 	{
279 		/* need to malloc additional space */
280 		buflen = len;
281 		if (buf != NULL)
282 			free(buf);
283 		buf = xalloc(buflen);
284 	}
285 
286 	bp = buf;
287 	if (av == NULL)
288 	{
289 		bcopy(s, bp, slen);
290 		bp += slen;
291 	}
292 	else
293 	{
294 		while (--slen >= 0 && (c = *s++) != '\0')
295 		{
296 			if (c != '%')
297 			{
298   pushc:
299 				*bp++ = c;
300 				continue;
301 			}
302 			if (--slen < 0 || (c = *s++) == '\0')
303 				c = '%';
304 			if (c == '%')
305 				goto pushc;
306 			if (!(isascii(c) && isdigit(c)))
307 			{
308 				*bp++ = '%';
309 				goto pushc;
310 			}
311 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
312 				continue;
313 			if (*avp == NULL)
314 				continue;
315 
316 			/* transliterate argument into output string */
317 			for (ap = *avp; (c = *ap++) != '\0'; )
318 				*bp++ = c;
319 		}
320 	}
321 	if (map->map_app != NULL)
322 		strcpy(bp, map->map_app);
323 	else
324 		*bp = '\0';
325 	if (tTd(39, 1))
326 		printf("map_rewrite => %s\n", buf);
327 	return buf;
328 }
329 /*
330 **  INITMAPS -- initialize for aliasing
331 **
332 **	Parameters:
333 **		rebuild -- if TRUE, this rebuilds the cached versions.
334 **		e -- current envelope.
335 **
336 **	Returns:
337 **		none.
338 **
339 **	Side Effects:
340 **		initializes aliases:
341 **		if NDBM:  opens the database.
342 **		if ~NDBM: reads the aliases into the symbol table.
343 */
344 
345 initmaps(rebuild, e)
346 	bool rebuild;
347 	register ENVELOPE *e;
348 {
349 	extern void map_init();
350 
351 #ifdef XDEBUG
352 	checkfd012("entering initmaps");
353 #endif
354 	CurEnv = e;
355 	if (rebuild)
356 	{
357 		stabapply(map_init, 1);
358 		stabapply(map_init, 2);
359 	}
360 	else
361 	{
362 		stabapply(map_init, 0);
363 	}
364 #ifdef XDEBUG
365 	checkfd012("exiting initmaps");
366 #endif
367 }
368 
369 void
370 map_init(s, rebuild)
371 	register STAB *s;
372 	int rebuild;
373 {
374 	register MAP *map;
375 
376 	/* has to be a map */
377 	if (s->s_type != ST_MAP)
378 		return;
379 
380 	map = &s->s_map;
381 	if (!bitset(MF_VALID, map->map_mflags))
382 		return;
383 
384 	if (tTd(38, 2))
385 		printf("map_init(%s:%s, %s, %d)\n",
386 			map->map_class->map_cname == NULL ? "NULL" :
387 				map->map_class->map_cname,
388 			map->map_mname == NULL ? "NULL" : map->map_mname,
389 			map->map_file == NULL ? "NULL" : map->map_file,
390 			rebuild);
391 
392 	if (rebuild == (bitset(MF_ALIAS, map->map_mflags) &&
393 		    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) ? 1 : 2))
394 	{
395 		if (tTd(38, 3))
396 			printf("\twrong pass\n");
397 		return;
398 	}
399 
400 	/* if already open, close it (for nested open) */
401 	if (bitset(MF_OPEN, map->map_mflags))
402 	{
403 		map->map_class->map_close(map);
404 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
405 	}
406 
407 	if (rebuild == 2)
408 	{
409 		rebuildaliases(map, FALSE);
410 	}
411 	else
412 	{
413 		if (map->map_class->map_open(map, O_RDONLY))
414 		{
415 			if (tTd(38, 4))
416 				printf("\t%s:%s %s: valid\n",
417 					map->map_class->map_cname == NULL ? "NULL" :
418 						map->map_class->map_cname,
419 					map->map_mname == NULL ? "NULL" :
420 						map->map_mname,
421 					map->map_file == NULL ? "NULL" :
422 						map->map_file);
423 			map->map_mflags |= MF_OPEN;
424 		}
425 		else
426 		{
427 			if (tTd(38, 4))
428 				printf("\t%s:%s %s: invalid: %s\n",
429 					map->map_class->map_cname == NULL ? "NULL" :
430 						map->map_class->map_cname,
431 					map->map_mname == NULL ? "NULL" :
432 						map->map_mname,
433 					map->map_file == NULL ? "NULL" :
434 						map->map_file,
435 					errstring(errno));
436 			if (!bitset(MF_OPTIONAL, map->map_mflags))
437 			{
438 				extern MAPCLASS BogusMapClass;
439 
440 				map->map_class = &BogusMapClass;
441 				map->map_mflags |= MF_OPEN;
442 			}
443 		}
444 	}
445 }
446 /*
447 **  NDBM modules
448 */
449 
450 #ifdef NDBM
451 
452 /*
453 **  DBM_MAP_OPEN -- DBM-style map open
454 */
455 
456 bool
457 ndbm_map_open(map, mode)
458 	MAP *map;
459 	int mode;
460 {
461 	register DBM *dbm;
462 	struct stat st;
463 
464 	if (tTd(38, 2))
465 		printf("ndbm_map_open(%s, %s, %d)\n",
466 			map->map_mname, map->map_file, mode);
467 
468 	if (mode == O_RDWR)
469 		mode |= O_CREAT|O_TRUNC;
470 
471 	/* open the database */
472 	dbm = dbm_open(map->map_file, mode, DBMMODE);
473 	if (dbm == NULL)
474 	{
475 		if (aliaswait(map, ".pag", FALSE))
476 			return TRUE;
477 		if (!bitset(MF_OPTIONAL, map->map_mflags))
478 			syserr("Cannot open DBM database %s", map->map_file);
479 		return FALSE;
480 	}
481 	map->map_db1 = (void *) dbm;
482 	if (mode == O_RDONLY)
483 	{
484 		if (bitset(MF_ALIAS, map->map_mflags) &&
485 		    !aliaswait(map, ".pag", TRUE))
486 			return FALSE;
487 	}
488 	else
489 	{
490 		int fd;
491 
492 		/* exclusive lock for duration of rebuild */
493 		fd = dbm_dirfno((DBM *) map->map_db1);
494 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags) &&
495 		    lockfile(fd, map->map_file, ".dir", LOCK_EX))
496 			map->map_mflags |= MF_LOCKED;
497 	}
498 	if (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0)
499 		map->map_mtime = st.st_mtime;
500 	return TRUE;
501 }
502 
503 
504 /*
505 **  DBM_MAP_LOOKUP -- look up a datum in a DBM-type map
506 */
507 
508 char *
509 ndbm_map_lookup(map, name, av, statp)
510 	MAP *map;
511 	char *name;
512 	char **av;
513 	int *statp;
514 {
515 	datum key, val;
516 	int fd;
517 	char keybuf[MAXNAME + 1];
518 
519 	if (tTd(38, 20))
520 		printf("ndbm_map_lookup(%s, %s)\n",
521 			map->map_mname, name);
522 
523 	key.dptr = name;
524 	key.dsize = strlen(name);
525 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
526 	{
527 		if (key.dsize > sizeof keybuf - 1)
528 			key.dsize = sizeof keybuf - 1;
529 		bcopy(key.dptr, keybuf, key.dsize + 1);
530 		makelower(keybuf);
531 		key.dptr = keybuf;
532 	}
533 	fd = dbm_dirfno((DBM *) map->map_db1);
534 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
535 		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
536 	val.dptr = NULL;
537 	if (bitset(MF_TRY0NULL, map->map_mflags))
538 	{
539 		val = dbm_fetch((DBM *) map->map_db1, key);
540 		if (val.dptr != NULL)
541 			map->map_mflags &= ~MF_TRY1NULL;
542 	}
543 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
544 	{
545 		key.dsize++;
546 		val = dbm_fetch((DBM *) map->map_db1, key);
547 		if (val.dptr != NULL)
548 			map->map_mflags &= ~MF_TRY0NULL;
549 	}
550 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
551 		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
552 	if (val.dptr == NULL)
553 		return NULL;
554 	if (bitset(MF_MATCHONLY, map->map_mflags))
555 		return map_rewrite(map, name, strlen(name), NULL);
556 	else
557 		return map_rewrite(map, val.dptr, val.dsize, av);
558 }
559 
560 
561 /*
562 **  DBM_MAP_STORE -- store a datum in the database
563 */
564 
565 void
566 ndbm_map_store(map, lhs, rhs)
567 	register MAP *map;
568 	char *lhs;
569 	char *rhs;
570 {
571 	datum key;
572 	datum data;
573 	int stat;
574 
575 	if (tTd(38, 12))
576 		printf("ndbm_map_store(%s, %s, %s)\n",
577 			map->map_mname, lhs, rhs);
578 
579 	key.dsize = strlen(lhs);
580 	key.dptr = lhs;
581 
582 	data.dsize = strlen(rhs);
583 	data.dptr = rhs;
584 
585 	if (bitset(MF_INCLNULL, map->map_mflags))
586 	{
587 		key.dsize++;
588 		data.dsize++;
589 	}
590 
591 	stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
592 	if (stat > 0)
593 	{
594 		if (!bitset(MF_APPEND, map->map_mflags))
595 			usrerr("050 Warning: duplicate alias name %s", lhs);
596 		else
597 		{
598 			static char *buf = NULL;
599 			static int bufsiz = 0;
600 			auto int xstat;
601 			datum old;
602 
603 			old.dptr = ndbm_map_lookup(map, key.dptr, NULL, &xstat);
604 			if (old.dptr != NULL && *old.dptr != '\0')
605 			{
606 				old.dsize = strlen(old.dptr);
607 				if (data.dsize + old.dsize + 2 > bufsiz)
608 				{
609 					if (buf != NULL)
610 						(void) free(buf);
611 					bufsiz = data.dsize + old.dsize + 2;
612 					buf = xalloc(bufsiz);
613 				}
614 				sprintf(buf, "%s,%s", data.dptr, old.dptr);
615 				data.dsize = data.dsize + old.dsize + 1;
616 				data.dptr = buf;
617 				if (tTd(38, 9))
618 					printf("ndbm_map_store append=%s\n", data.dptr);
619 			}
620 		}
621 		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
622 	}
623 	if (stat != 0)
624 		syserr("readaliases: dbm put (%s)", lhs);
625 }
626 
627 
628 /*
629 **  NDBM_MAP_CLOSE -- close the database
630 */
631 
632 void
633 ndbm_map_close(map)
634 	register MAP  *map;
635 {
636 	if (tTd(38, 9))
637 		printf("ndbm_map_close(%s, %s, %x)\n",
638 			map->map_mname, map->map_file, map->map_mflags);
639 
640 	if (bitset(MF_WRITABLE, map->map_mflags))
641 	{
642 #ifdef NIS
643 		bool inclnull;
644 		char buf[200];
645 
646 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
647 		map->map_mflags &= ~MF_INCLNULL;
648 
649 		(void) sprintf(buf, "%010ld", curtime());
650 		ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
651 
652 		(void) gethostname(buf, sizeof buf);
653 		ndbm_map_store(map, "YP_MASTER_NAME", buf);
654 
655 		if (inclnull)
656 			map->map_mflags |= MF_INCLNULL;
657 #endif
658 
659 		/* write out the distinguished alias */
660 		ndbm_map_store(map, "@", "@");
661 	}
662 	dbm_close((DBM *) map->map_db1);
663 }
664 
665 #endif
666 /*
667 **  NEWDB (Hash and BTree) Modules
668 */
669 
670 #ifdef NEWDB
671 
672 /*
673 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
674 **
675 **	These do rather bizarre locking.  If you can lock on open,
676 **	do that to avoid the condition of opening a database that
677 **	is being rebuilt.  If you don't, we'll try to fake it, but
678 **	there will be a race condition.  If opening for read-only,
679 **	we immediately release the lock to avoid freezing things up.
680 **	We really ought to hold the lock, but guarantee that we won't
681 **	be pokey about it.  That's hard to do.
682 */
683 
684 bool
685 bt_map_open(map, mode)
686 	MAP *map;
687 	int mode;
688 {
689 	DB *db;
690 	int i;
691 	int omode;
692 	int fd;
693 	struct stat st;
694 	char buf[MAXNAME + 1];
695 
696 	if (tTd(38, 2))
697 		printf("bt_map_open(%s, %s, %d)\n",
698 			map->map_mname, map->map_file, mode);
699 
700 	omode = mode;
701 	if (omode == O_RDWR)
702 	{
703 		omode |= O_CREAT|O_TRUNC;
704 #if defined(O_EXLOCK) && HASFLOCK
705 		omode |= O_EXLOCK;
706 # if !OLD_NEWDB
707 	}
708 	else
709 	{
710 		omode |= O_SHLOCK;
711 # endif
712 #endif
713 	}
714 
715 	(void) strcpy(buf, map->map_file);
716 	i = strlen(buf);
717 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
718 		(void) strcat(buf, ".db");
719 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
720 	if (db == NULL)
721 	{
722 #ifdef MAYBENEXTRELEASE
723 		if (aliaswait(map, ".db", FALSE))
724 			return TRUE;
725 #endif
726 		if (!bitset(MF_OPTIONAL, map->map_mflags))
727 			syserr("Cannot open BTREE database %s", map->map_file);
728 		return FALSE;
729 	}
730 #if !OLD_NEWDB
731 	fd = db->fd(db);
732 # if defined(O_EXLOCK) && HASFLOCK
733 	if (fd >= 0)
734 	{
735 		if (mode == O_RDONLY)
736 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
737 		else
738 			map->map_mflags |= MF_LOCKED;
739 	}
740 # else
741 	if (mode == O_RDWR && fd >= 0)
742 	{
743 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
744 			map->map_mflags |= MF_LOCKED;
745 	}
746 # endif
747 #endif
748 
749 	/* try to make sure that at least the database header is on disk */
750 	if (mode == O_RDWR)
751 #if OLD_NEWDB
752 		(void) db->sync(db);
753 #else
754 		(void) db->sync(db, 0);
755 
756 	if (fd >= 0 && fstat(fd, &st) >= 0)
757 		map->map_mtime = st.st_mtime;
758 #endif
759 
760 	map->map_db2 = (void *) db;
761 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
762 		if (!aliaswait(map, ".db", TRUE))
763 			return FALSE;
764 	return TRUE;
765 }
766 
767 
768 /*
769 **  HASH_MAP_INIT -- HASH-style map initialization
770 */
771 
772 bool
773 hash_map_open(map, mode)
774 	MAP *map;
775 	int mode;
776 {
777 	DB *db;
778 	int i;
779 	int omode;
780 	int fd;
781 	struct stat st;
782 	char buf[MAXNAME + 1];
783 
784 	if (tTd(38, 2))
785 		printf("hash_map_open(%s, %s, %d)\n",
786 			map->map_mname, map->map_file, mode);
787 
788 	omode = mode;
789 	if (omode == O_RDWR)
790 	{
791 		omode |= O_CREAT|O_TRUNC;
792 #if defined(O_EXLOCK) && HASFLOCK
793 		omode |= O_EXLOCK;
794 # if !OLD_NEWDB
795 	}
796 	else
797 	{
798 		omode |= O_SHLOCK;
799 # endif
800 #endif
801 	}
802 
803 	(void) strcpy(buf, map->map_file);
804 	i = strlen(buf);
805 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
806 		(void) strcat(buf, ".db");
807 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
808 	if (db == NULL)
809 	{
810 #ifdef MAYBENEXTRELEASE
811 		if (aliaswait(map, ".db", FALSE))
812 			return TRUE;
813 #endif
814 		if (!bitset(MF_OPTIONAL, map->map_mflags))
815 			syserr("Cannot open HASH database %s", map->map_file);
816 		return FALSE;
817 	}
818 #if !OLD_NEWDB
819 	fd = db->fd(db);
820 # if defined(O_EXLOCK) && HASFLOCK
821 	if (fd >= 0)
822 	{
823 		if (mode == O_RDONLY)
824 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
825 		else
826 			map->map_mflags |= MF_LOCKED;
827 	}
828 # else
829 	if (mode == O_RDWR && fd >= 0)
830 	{
831 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
832 			map->map_mflags |= MF_LOCKED;
833 	}
834 # endif
835 #endif
836 
837 	/* try to make sure that at least the database header is on disk */
838 	if (mode == O_RDWR)
839 #if OLD_NEWDB
840 		(void) db->sync(db);
841 #else
842 		(void) db->sync(db, 0);
843 
844 	if (fd >= 0 && fstat(fd, &st) >= 0)
845 		map->map_mtime = st.st_mtime;
846 #endif
847 
848 	map->map_db2 = (void *) db;
849 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
850 		if (!aliaswait(map, ".db", TRUE))
851 			return FALSE;
852 	return TRUE;
853 }
854 
855 
856 /*
857 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
858 */
859 
860 char *
861 db_map_lookup(map, name, av, statp)
862 	MAP *map;
863 	char *name;
864 	char **av;
865 	int *statp;
866 {
867 	DBT key, val;
868 	register DB *db = (DB *) map->map_db2;
869 	int st;
870 	int saveerrno;
871 	int fd;
872 	char keybuf[MAXNAME + 1];
873 
874 	if (tTd(38, 20))
875 		printf("db_map_lookup(%s, %s)\n",
876 			map->map_mname, name);
877 
878 	key.size = strlen(name);
879 	if (key.size > sizeof keybuf - 1)
880 		key.size = sizeof keybuf - 1;
881 	key.data = keybuf;
882 	bcopy(name, keybuf, key.size + 1);
883 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
884 		makelower(keybuf);
885 #if !OLD_NEWDB
886 	fd = db->fd(db);
887 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
888 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
889 #endif
890 	st = 1;
891 	if (bitset(MF_TRY0NULL, map->map_mflags))
892 	{
893 		st = db->get(db, &key, &val, 0);
894 		if (st == 0)
895 			map->map_mflags &= ~MF_TRY1NULL;
896 	}
897 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
898 	{
899 		key.size++;
900 		st = db->get(db, &key, &val, 0);
901 		if (st == 0)
902 			map->map_mflags &= ~MF_TRY0NULL;
903 	}
904 	saveerrno = errno;
905 #if !OLD_NEWDB
906 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
907 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
908 #endif
909 	if (st != 0)
910 	{
911 		errno = saveerrno;
912 		if (st < 0)
913 			syserr("db_map_lookup: get (%s)", name);
914 		return NULL;
915 	}
916 	if (bitset(MF_MATCHONLY, map->map_mflags))
917 		return map_rewrite(map, name, strlen(name), NULL);
918 	else
919 		return map_rewrite(map, val.data, val.size, av);
920 }
921 
922 
923 /*
924 **  DB_MAP_STORE -- store a datum in the NEWDB database
925 */
926 
927 void
928 db_map_store(map, lhs, rhs)
929 	register MAP *map;
930 	char *lhs;
931 	char *rhs;
932 {
933 	int stat;
934 	DBT key;
935 	DBT data;
936 	register DB *db = map->map_db2;
937 
938 	if (tTd(38, 20))
939 		printf("db_map_store(%s, %s, %s)\n",
940 			map->map_mname, lhs, rhs);
941 
942 	key.size = strlen(lhs);
943 	key.data = lhs;
944 
945 	data.size = strlen(rhs);
946 	data.data = rhs;
947 
948 	if (bitset(MF_INCLNULL, map->map_mflags))
949 	{
950 		key.size++;
951 		data.size++;
952 	}
953 
954 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
955 	if (stat > 0)
956 	{
957 		if (!bitset(MF_APPEND, map->map_mflags))
958 			usrerr("050 Warning: duplicate alias name %s", lhs);
959 		else
960 		{
961 			static char *buf = NULL;
962 			static int bufsiz = 0;
963 			DBT old;
964 
965 			old.data = db_map_lookup(map, key.data, NULL, &stat);
966 			if (old.data != NULL)
967 			{
968 				old.size = strlen(old.data);
969 				if (data.size + old.size + 2 > bufsiz)
970 				{
971 					if (buf != NULL)
972 						(void) free(buf);
973 					bufsiz = data.size + old.size + 2;
974 					buf = xalloc(bufsiz);
975 				}
976 				sprintf(buf, "%s,%s", data.data, old.data);
977 				data.size = data.size + old.size + 1;
978 				data.data = buf;
979 				if (tTd(38, 9))
980 					printf("db_map_store append=%s\n", data.data);
981 			}
982 		}
983 		stat = db->put(db, &key, &data, 0);
984 	}
985 	if (stat != 0)
986 		syserr("readaliases: db put (%s)", lhs);
987 }
988 
989 
990 /*
991 **  DB_MAP_CLOSE -- add distinguished entries and close the database
992 */
993 
994 void
995 db_map_close(map)
996 	MAP *map;
997 {
998 	register DB *db = map->map_db2;
999 
1000 	if (tTd(38, 9))
1001 		printf("db_map_close(%s, %s, %x)\n",
1002 			map->map_mname, map->map_file, map->map_mflags);
1003 
1004 	if (bitset(MF_WRITABLE, map->map_mflags))
1005 	{
1006 		/* write out the distinguished alias */
1007 		db_map_store(map, "@", "@");
1008 	}
1009 
1010 	if (db->close(db) != 0)
1011 		syserr("readaliases: db close failure");
1012 }
1013 
1014 #endif
1015 /*
1016 **  NIS Modules
1017 */
1018 
1019 # ifdef NIS
1020 
1021 # ifndef YPERR_BUSY
1022 #  define YPERR_BUSY	16
1023 # endif
1024 
1025 /*
1026 **  NIS_MAP_OPEN -- open DBM map
1027 */
1028 
1029 bool
1030 nis_map_open(map, mode)
1031 	MAP *map;
1032 	int mode;
1033 {
1034 	int yperr;
1035 	register char *p;
1036 	auto char *vp;
1037 	auto int vsize;
1038 	char *master;
1039 
1040 	if (tTd(38, 2))
1041 		printf("nis_map_open(%s, %s)\n",
1042 			map->map_mname, map->map_file);
1043 
1044 	if (mode != O_RDONLY)
1045 	{
1046 		/* issue a pseudo-error message */
1047 #ifdef ENOSYS
1048 		errno = ENOSYS;
1049 #else
1050 # ifdef EFTYPE
1051 		errno = EFTYPE;
1052 # else
1053 		errno = ENXIO;
1054 # endif
1055 #endif
1056 		return FALSE;
1057 	}
1058 
1059 	p = strchr(map->map_file, '@');
1060 	if (p != NULL)
1061 	{
1062 		*p++ = '\0';
1063 		if (*p != '\0')
1064 			map->map_domain = p;
1065 	}
1066 
1067 	if (*map->map_file == '\0')
1068 		map->map_file = "mail.aliases";
1069 
1070 	if (map->map_domain == NULL)
1071 	{
1072 		yperr = yp_get_default_domain(&map->map_domain);
1073 		if (yperr != 0)
1074 		{
1075 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1076 				syserr("421 NIS map %s specified, but NIS not running\n",
1077 					map->map_file);
1078 			return FALSE;
1079 		}
1080 	}
1081 
1082 	/* check to see if this map actually exists */
1083 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1084 			&vp, &vsize);
1085 	if (tTd(38, 10))
1086 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1087 			map->map_domain, map->map_file, yperr_string(yperr));
1088 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1089 	{
1090 		if (!bitset(MF_ALIAS, map->map_mflags) ||
1091 		    aliaswait(map, NULL, TRUE))
1092 			return TRUE;
1093 	}
1094 
1095 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1096 	{
1097 		syserr("421 Cannot bind to map %s in domain %s: %s",
1098 			map->map_file, map->map_domain, yperr_string(yperr));
1099 	}
1100 
1101 	return FALSE;
1102 }
1103 
1104 
1105 /*
1106 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1107 */
1108 
1109 char *
1110 nis_map_lookup(map, name, av, statp)
1111 	MAP *map;
1112 	char *name;
1113 	char **av;
1114 	int *statp;
1115 {
1116 	char *vp;
1117 	auto int vsize;
1118 	int buflen;
1119 	int yperr;
1120 	char keybuf[MAXNAME + 1];
1121 
1122 	if (tTd(38, 20))
1123 		printf("nis_map_lookup(%s, %s)\n",
1124 			map->map_mname, name);
1125 
1126 	buflen = strlen(name);
1127 	if (buflen > sizeof keybuf - 1)
1128 		buflen = sizeof keybuf - 1;
1129 	bcopy(name, keybuf, buflen + 1);
1130 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1131 		makelower(keybuf);
1132 	yperr = YPERR_KEY;
1133 	if (bitset(MF_TRY0NULL, map->map_mflags))
1134 	{
1135 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1136 			     &vp, &vsize);
1137 		if (yperr == 0)
1138 			map->map_mflags &= ~MF_TRY1NULL;
1139 	}
1140 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1141 	{
1142 		buflen++;
1143 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1144 			     &vp, &vsize);
1145 		if (yperr == 0)
1146 			map->map_mflags &= ~MF_TRY0NULL;
1147 	}
1148 	if (yperr != 0)
1149 	{
1150 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1151 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1152 		return NULL;
1153 	}
1154 	if (bitset(MF_MATCHONLY, map->map_mflags))
1155 		return map_rewrite(map, name, strlen(name), NULL);
1156 	else
1157 		return map_rewrite(map, vp, vsize, av);
1158 }
1159 
1160 #endif
1161 /*
1162 **  NISPLUS Modules
1163 **
1164 **	This code donated by Sun Microsystems.
1165 */
1166 
1167 #ifdef NISPLUS
1168 
1169 #undef NIS /* symbol conflict in nis.h */
1170 #include <rpcsvc/nis.h>
1171 #include <rpcsvc/nislib.h>
1172 
1173 #define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1174 #define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1175 #define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1176 #define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
1177 
1178 /*
1179 **  NISPLUS_MAP_OPEN -- open nisplus table
1180 */
1181 
1182 bool
1183 nisplus_map_open(map, mode)
1184 	MAP *map;
1185 	int mode;
1186 {
1187 	register char *p;
1188 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1189 	nis_result *res = NULL;
1190 	u_int objs_len;
1191 	nis_object *obj_ptr;
1192 	int retry_cnt, max_col, i;
1193 
1194 	if (tTd(38, 2))
1195 		printf("nisplus_map_open(%s, %s, %d)\n",
1196 			map->map_mname, map->map_file, mode);
1197 
1198 	if (mode != O_RDONLY)
1199 	{
1200 		errno = ENODEV;
1201 		return FALSE;
1202 	}
1203 
1204 	if (*map->map_file == '\0')
1205 		map->map_file = "mail_aliases.org_dir";
1206 
1207 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1208 	{
1209 		/* set default NISPLUS Domain to $m */
1210 		extern char *nisplus_default_domain();
1211 
1212 		map->map_domain = newstr(nisplus_default_domain());
1213 		if (tTd(38, 2))
1214 			printf("nisplus_map_open(%s): using domain %s\n",
1215 				 map->map_file, map->map_domain);
1216 	}
1217 	if (!PARTIAL_NAME(map->map_file))
1218 		map->map_domain = newstr("");
1219 
1220 	/* check to see if this map actually exists */
1221 	if (PARTIAL_NAME(map->map_file))
1222 		sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
1223 	else
1224 		strcpy(qbuf, map->map_file);
1225 
1226 	retry_cnt = 0;
1227 	while (res == NULL || res->status != NIS_SUCCESS)
1228 	{
1229 		res = nis_lookup(qbuf, FOLLOW_LINKS);
1230 		switch (res->status)
1231 		{
1232 		  case NIS_SUCCESS:
1233 		  case NIS_TRYAGAIN:
1234 		  case NIS_RPCERROR:
1235 		  case NIS_NAMEUNREACHABLE:
1236 			break;
1237 
1238 		  default:		/* all other nisplus errors */
1239 #if 0
1240 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1241 				syserr("421 Cannot find table %s.%s: %s",
1242 					map->map_file, map->map_domain,
1243 					nis_sperrno(res->status));
1244 #endif
1245 			errno = EBADR;
1246 			return FALSE;
1247 		}
1248 		sleep(2);		/* try not to overwhelm hosed server */
1249 		if (retry_cnt++ > 4)
1250 		{
1251 			errno = EBADR;
1252 			return FALSE;
1253 		}
1254 	}
1255 
1256 	if (NIS_RES_NUMOBJ(res) != 1 ||
1257 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1258 	{
1259 		if (tTd(38, 10))
1260 			printf("nisplus_map_open: %s is not a table\n", qbuf);
1261 #if 0
1262 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1263 			syserr("421 %s.%s: %s is not a table",
1264 				map->map_file, map->map_domain,
1265 				nis_sperrno(res->status));
1266 #endif
1267 		errno = EBADR;
1268 		return FALSE;
1269 	}
1270 	/* default key column is column 0 */
1271 	if (map->map_keycolnm == NULL)
1272 		map->map_keycolnm = newstr(COL_NAME(res,0));
1273 
1274 	max_col = COL_MAX(res);
1275 
1276 	/* verify the key column exist */
1277 	for (i=0; i< max_col; i++)
1278 	{
1279 		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
1280 			break;
1281 	}
1282 	if (i == max_col)
1283 	{
1284 		if (tTd(38, 2))
1285 			printf("nisplus_map_open(%s): can not find key column %s\n",
1286 				map->map_file, map->map_keycolnm);
1287 		errno = EBADR;
1288 		return FALSE;
1289 	}
1290 
1291 	/* default value column is the last column */
1292 	if (map->map_valcolnm == NULL)
1293 	{
1294 		map->map_valcolno = max_col - 1;
1295 		return TRUE;
1296 	}
1297 
1298 	for (i=0; i< max_col; i++)
1299 	{
1300 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1301 		{
1302 			map->map_valcolno = i;
1303 			return TRUE;
1304 		}
1305 	}
1306 
1307 	if (tTd(38, 2))
1308 		printf("nisplus_map_open(%s): can not find column %s\n",
1309 			 map->map_file, map->map_keycolnm);
1310 	errno = EBADR;
1311 	return FALSE;
1312 }
1313 
1314 
1315 /*
1316 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1317 */
1318 
1319 char *
1320 nisplus_map_lookup(map, name, av, statp)
1321 	MAP *map;
1322 	char *name;
1323 	char **av;
1324 	int *statp;
1325 {
1326 	char *vp;
1327 	auto int vsize;
1328 	int buflen;
1329 	char search_key[MAXNAME + 1];
1330 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1331 	nis_result *result;
1332 
1333 	if (tTd(38, 20))
1334 		printf("nisplus_map_lookup(%s, %s)\n",
1335 			map->map_mname, name);
1336 
1337 	if (!bitset(MF_OPEN, map->map_mflags))
1338 	{
1339 		if (nisplus_map_open(map, O_RDONLY))
1340 			map->map_mflags |= MF_OPEN;
1341 		else
1342 		{
1343 			*statp = EX_UNAVAILABLE;
1344 			return NULL;
1345 		}
1346 	}
1347 
1348 	buflen = strlen(name);
1349 	if (buflen > sizeof search_key - 1)
1350 		buflen = sizeof search_key - 1;
1351 	bcopy(name, search_key, buflen + 1);
1352 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1353 		makelower(search_key);
1354 
1355 	/* construct the query */
1356 	if (PARTIAL_NAME(map->map_file))
1357 		sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
1358 			search_key, map->map_file, map->map_domain);
1359 	else
1360 		sprintf(qbuf, "[%s=%s],%s", map->map_keycolnm,
1361 			search_key, map->map_file);
1362 
1363 	if (tTd(38, 20))
1364 		printf("qbuf=%s\n", qbuf);
1365 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1366 	if (result->status == NIS_SUCCESS)
1367 	{
1368 		int count;
1369 		char *str;
1370 
1371 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1372 		{
1373 			if (LogLevel > 10)
1374 				syslog(LOG_WARNING,
1375 				  "%s:Lookup error, expected 1 entry, got (%d)",
1376 				    map->map_file, count);
1377 
1378 			/* ignore second entry */
1379 			if (tTd(38, 20))
1380 				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1381 					name, count);
1382 		}
1383 
1384 		vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1385 		/* set the length of the result */
1386 		if (vp == NULL)
1387 			vp = "";
1388 		vsize = strlen(vp);
1389 		if (tTd(38, 20))
1390 			printf("nisplus_map_lookup(%s), found %s\n",
1391 				name, vp);
1392 		if (bitset(MF_MATCHONLY, map->map_mflags))
1393 			str = map_rewrite(map, name, strlen(name), NULL);
1394 		else
1395 			str = map_rewrite(map, vp, vsize, av);
1396 		nis_freeresult(result);
1397 #ifdef MAP_EXIT_STAT
1398 		*statp = EX_OK;
1399 #endif
1400 		return str;
1401 	}
1402 	else
1403 	{
1404 #ifdef MAP_EXIT_STAT
1405 		if (result->status == NIS_NOTFOUND)
1406 			*statp = EX_NOTFOUND;
1407 		else if (result->status == NIS_TRYAGAIN)
1408 			*statp = EX_TEMPFAIL;
1409 		else
1410 		{
1411 			*statp = EX_UNAVAILABLE;
1412 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1413 		}
1414 #else
1415 		if ((result->status != NIS_NOTFOUND) &&
1416 		    (result->status != NIS_TRYAGAIN))
1417 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1418 #endif
1419 	}
1420 	if (tTd(38, 20))
1421 		printf("nisplus_map_lookup(%s), failed\n", name);
1422 	nis_freeresult(result);
1423 	return NULL;
1424 }
1425 
1426 
1427 char *
1428 nisplus_default_domain()
1429 {
1430 	static char default_domain[MAXNAME + 1] = "";
1431 	char *p;
1432 
1433 	if (default_domain[0] != '\0')
1434 		return(default_domain);
1435 
1436 	p = nis_local_directory();
1437 	strcpy(default_domain, p);
1438 	return default_domain;
1439 }
1440 
1441 #endif /* NISPLUS */
1442 /*
1443 **  HESIOD Modules
1444 */
1445 
1446 #ifdef HESIOD
1447 
1448 #include <hesiod.h>
1449 
1450 char *
1451 hes_map_lookup(map, name, av, statp)
1452         MAP *map;
1453         char *name;
1454         char **av;
1455         int *statp;
1456 {
1457 	char **hp;
1458 	char *retdata = NULL;
1459 	int i;
1460 
1461 	if (tTd(38, 20))
1462 		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1463 
1464 	hp = hes_resolve(name, map->map_file);
1465 	if (hp == NULL)
1466 		return NULL;
1467 
1468 	if (hp[0] != NULL)
1469 	{
1470 		if (bitset(MF_MATCHONLY, map->map_mflags))
1471 			retdata = map_rewrite(map, name, strlen(name), NULL);
1472 		else
1473 			retdata = map_rewrite(map, hp[0], strlen(hp[0]), av);
1474 	}
1475 
1476 	for (i = 0; hp[i] != NULL; i++)
1477 		free(hp[i]);
1478 	free(hp);
1479 	return retdata;
1480 }
1481 
1482 #endif
1483 /*
1484 **  NeXT NETINFO Modules
1485 */
1486 
1487 #ifdef NETINFO
1488 
1489 #define NETINFO_DEFAULT_DIR		"/aliases"
1490 #define NETINFO_DEFAULT_PROPERTY	"members"
1491 
1492 
1493 /*
1494 **  NI_MAP_OPEN -- open NetInfo Aliases
1495 */
1496 
1497 bool
1498 ni_map_open(map, mode)
1499 	MAP *map;
1500 	int mode;
1501 {
1502 	char *p;
1503 
1504 	if (tTd(38, 20))
1505 		printf("ni_map_open: %s\n", map->map_file);
1506 
1507 	if (*map->map_file == '\0')
1508 		map->map_file = NETINFO_DEFAULT_DIR;
1509 
1510 	if (map->map_valcolnm == NULL)
1511 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1512 
1513 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1514 		map->map_coldelim = ',';
1515 
1516 	return TRUE;
1517 }
1518 
1519 
1520 /*
1521 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1522 */
1523 
1524 char *
1525 ni_map_lookup(map, name, av, statp)
1526 	MAP *map;
1527 	char *name;
1528 	char **av;
1529 	int *statp;
1530 {
1531 	char *res;
1532 	char *propval;
1533 	extern char *ni_propval();
1534 
1535 	if (tTd(38, 20))
1536 		printf("ni_map_lookup(%s, %s)\n",
1537 			map->map_mname, name);
1538 
1539 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1540 			     map->map_valcolnm, map->map_coldelim);
1541 
1542 	if (propval == NULL)
1543 		return NULL;
1544 
1545 	if (bitset(MF_MATCHONLY, map->map_mflags))
1546 		res = map_rewrite(map, name, strlen(name), NULL);
1547 	else
1548 		res = map_rewrite(map, propval, strlen(propval), av);
1549 	free(propval);
1550 	return res;
1551 }
1552 
1553 #endif
1554 /*
1555 **  TEXT (unindexed text file) Modules
1556 **
1557 **	This code donated by Sun Microsystems.
1558 */
1559 
1560 
1561 /*
1562 **  TEXT_MAP_OPEN -- open text table
1563 */
1564 
1565 bool
1566 text_map_open(map, mode)
1567 	MAP *map;
1568 	int mode;
1569 {
1570 	struct stat sbuf;
1571 
1572 	if (tTd(38, 2))
1573 		printf("text_map_open(%s, %s, %d)\n",
1574 			map->map_mname, map->map_file, mode);
1575 
1576 	if (mode != O_RDONLY)
1577 	{
1578 		errno = ENODEV;
1579 		return FALSE;
1580 	}
1581 
1582 	if (*map->map_file == '\0')
1583 	{
1584 		if (tTd(38, 2))
1585 			printf("text_map_open: file name required\n");
1586 		return FALSE;
1587 	}
1588 
1589 	if (map->map_file[0] != '/')
1590 	{
1591 		if (tTd(38, 2))
1592 			printf("text_map_open(%s): file name must be fully qualified\n",
1593 				map->map_file);
1594 		return FALSE;
1595 	}
1596 	/* check to see if this map actually accessable */
1597 	if (access(map->map_file, R_OK) <0)
1598 		return FALSE;
1599 
1600 	/* check to see if this map actually exist */
1601 	if (stat(map->map_file, &sbuf) <0)
1602 	{
1603 		if (tTd(38, 2))
1604 			printf("text_map_open(%s): can not stat %s\n",
1605 				map->map_file, map->map_file);
1606 		return FALSE;
1607 	}
1608 
1609 	if (!S_ISREG(sbuf.st_mode))
1610 	{
1611 		if (tTd(38, 2))
1612 			printf("text_map_open(%s): %s is not a file\n",
1613 				map->map_file, map->map_file);
1614 		return FALSE;
1615 	}
1616 
1617 	if (map->map_keycolnm == NULL)
1618 		map->map_keycolno = 0;
1619 	else
1620 	{
1621 		if (!isdigit(*map->map_keycolnm))
1622 		{
1623 			if (tTd(38, 2))
1624 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1625 					map->map_file, map->map_keycolnm);
1626 			return FALSE;
1627 		}
1628 		map->map_keycolno = atoi(map->map_keycolnm);
1629 	}
1630 
1631 	if (map->map_valcolnm == NULL)
1632 		map->map_valcolno = 0;
1633 	else
1634 	{
1635 		if (!isdigit(*map->map_valcolnm))
1636 		{
1637 			if (tTd(38, 2))
1638 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1639 					map->map_file, map->map_valcolnm);
1640 			return FALSE;
1641 		}
1642 		map->map_valcolno = atoi(map->map_valcolnm);
1643 	}
1644 
1645 	if (tTd(38, 2))
1646 	{
1647 		printf("text_map_open(%s): delimiter = ",
1648 			map->map_file);
1649 		if (map->map_coldelim == '\0')
1650 			printf("(white space)\n");
1651 		else
1652 			printf("%c\n", map->map_coldelim);
1653 	}
1654 
1655 	return TRUE;
1656 }
1657 
1658 
1659 /*
1660 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1661 */
1662 
1663 char *
1664 text_map_lookup(map, name, av, statp)
1665 	MAP *map;
1666 	char *name;
1667 	char **av;
1668 	int *statp;
1669 {
1670 	char *vp;
1671 	auto int vsize;
1672 	int buflen;
1673 	char search_key[MAXNAME + 1];
1674 	char linebuf[MAXLINE];
1675 	FILE *f;
1676 	char buf[MAXNAME + 1];
1677 	char delim;
1678 	int key_idx;
1679 	bool found_it;
1680 	extern char *get_column();
1681 
1682 
1683 	found_it = FALSE;
1684 	if (tTd(38, 20))
1685 		printf("text_map_lookup(%s)\n", name);
1686 
1687 	buflen = strlen(name);
1688 	if (buflen > sizeof search_key - 1)
1689 		buflen = sizeof search_key - 1;
1690 	bcopy(name, search_key, buflen + 1);
1691 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1692 		makelower(search_key);
1693 
1694 	f = fopen(map->map_file, "r");
1695 	if (f == NULL)
1696 	{
1697 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1698 		*statp = EX_UNAVAILABLE;
1699 		return NULL;
1700 	}
1701 	key_idx = map->map_keycolno;
1702 	delim = map->map_coldelim;
1703 	while (fgets(linebuf, MAXLINE, f))
1704 	{
1705 		char *lf;
1706 		if (linebuf[0] == '#')
1707 			continue; /* skip comment line */
1708 		if (lf = strchr(linebuf, '\n'))
1709 			*lf = '\0';
1710 		if (!strcasecmp(search_key,
1711 				get_column(linebuf, key_idx, delim, buf)))
1712 		{
1713 			found_it = TRUE;
1714 			break;
1715 		}
1716 	}
1717 	fclose(f);
1718 	if (!found_it)
1719 	{
1720 #ifdef MAP_EXIT_STAT
1721 		*statp = EX_NOTFOUND;
1722 #endif
1723 		return(NULL);
1724 	}
1725 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1726 	vsize = strlen(vp);
1727 #ifdef MAP_EXIT_STAT
1728 	*statp = EX_OK;
1729 #endif
1730 	if (bitset(MF_MATCHONLY, map->map_mflags))
1731 		return map_rewrite(map, name, strlen(name), NULL);
1732 	else
1733 		return map_rewrite(map, vp, vsize, av);
1734 }
1735 /*
1736 **  STAB (Symbol Table) Modules
1737 */
1738 
1739 
1740 /*
1741 **  STAB_MAP_LOOKUP -- look up alias in symbol table
1742 */
1743 
1744 char *
1745 stab_map_lookup(map, name, av, pstat)
1746 	register MAP *map;
1747 	char *name;
1748 	char **av;
1749 	int *pstat;
1750 {
1751 	register STAB *s;
1752 
1753 	if (tTd(38, 20))
1754 		printf("stab_lookup(%s, %s)\n",
1755 			map->map_mname, name);
1756 
1757 	s = stab(name, ST_ALIAS, ST_FIND);
1758 	if (s != NULL)
1759 		return (s->s_alias);
1760 	return (NULL);
1761 }
1762 
1763 
1764 /*
1765 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1766 */
1767 
1768 void
1769 stab_map_store(map, lhs, rhs)
1770 	register MAP *map;
1771 	char *lhs;
1772 	char *rhs;
1773 {
1774 	register STAB *s;
1775 
1776 	s = stab(lhs, ST_ALIAS, ST_ENTER);
1777 	s->s_alias = newstr(rhs);
1778 }
1779 
1780 
1781 /*
1782 **  STAB_MAP_OPEN -- initialize (reads data file)
1783 **
1784 **	This is a wierd case -- it is only intended as a fallback for
1785 **	aliases.  For this reason, opens for write (only during a
1786 **	"newaliases") always fails, and opens for read open the
1787 **	actual underlying text file instead of the database.
1788 */
1789 
1790 bool
1791 stab_map_open(map, mode)
1792 	register MAP *map;
1793 	int mode;
1794 {
1795 	FILE *af;
1796 	struct stat st;
1797 
1798 	if (tTd(38, 2))
1799 		printf("stab_map_open(%s, %s)\n",
1800 			map->map_mname, map->map_file);
1801 
1802 	if (mode != O_RDONLY)
1803 	{
1804 		errno = ENODEV;
1805 		return FALSE;
1806 	}
1807 
1808 	af = fopen(map->map_file, "r");
1809 	if (af == NULL)
1810 		return FALSE;
1811 	readaliases(map, af, FALSE, FALSE);
1812 
1813 	if (fstat(fileno(af), &st) >= 0)
1814 		map->map_mtime = st.st_mtime;
1815 	fclose(af);
1816 
1817 	return TRUE;
1818 }
1819 /*
1820 **  Implicit Modules
1821 **
1822 **	Tries several types.  For back compatibility of aliases.
1823 */
1824 
1825 
1826 /*
1827 **  IMPL_MAP_LOOKUP -- lookup in best open database
1828 */
1829 
1830 char *
1831 impl_map_lookup(map, name, av, pstat)
1832 	MAP *map;
1833 	char *name;
1834 	char **av;
1835 	int *pstat;
1836 {
1837 	if (tTd(38, 20))
1838 		printf("impl_map_lookup(%s, %s)\n",
1839 			map->map_mname, name);
1840 
1841 #ifdef NEWDB
1842 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1843 		return db_map_lookup(map, name, av, pstat);
1844 #endif
1845 #ifdef NDBM
1846 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1847 		return ndbm_map_lookup(map, name, av, pstat);
1848 #endif
1849 	return stab_map_lookup(map, name, av, pstat);
1850 }
1851 
1852 /*
1853 **  IMPL_MAP_STORE -- store in open databases
1854 */
1855 
1856 void
1857 impl_map_store(map, lhs, rhs)
1858 	MAP *map;
1859 	char *lhs;
1860 	char *rhs;
1861 {
1862 #ifdef NEWDB
1863 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1864 		db_map_store(map, lhs, rhs);
1865 #endif
1866 #ifdef NDBM
1867 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1868 		ndbm_map_store(map, lhs, rhs);
1869 #endif
1870 	stab_map_store(map, lhs, rhs);
1871 }
1872 
1873 /*
1874 **  IMPL_MAP_OPEN -- implicit database open
1875 */
1876 
1877 bool
1878 impl_map_open(map, mode)
1879 	MAP *map;
1880 	int mode;
1881 {
1882 	struct stat stb;
1883 
1884 	if (tTd(38, 2))
1885 		printf("impl_map_open(%s, %s, %d)\n",
1886 			map->map_mname, map->map_file, mode);
1887 
1888 	if (stat(map->map_file, &stb) < 0)
1889 	{
1890 		/* no alias file at all */
1891 		if (tTd(38, 3))
1892 			printf("no map file\n");
1893 		return FALSE;
1894 	}
1895 
1896 #ifdef NEWDB
1897 	map->map_mflags |= MF_IMPL_HASH;
1898 	if (hash_map_open(map, mode))
1899 	{
1900 #if defined(NDBM) && defined(NIS)
1901 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
1902 #endif
1903 			return TRUE;
1904 	}
1905 	else
1906 		map->map_mflags &= ~MF_IMPL_HASH;
1907 #endif
1908 #ifdef NDBM
1909 	map->map_mflags |= MF_IMPL_NDBM;
1910 	if (ndbm_map_open(map, mode))
1911 	{
1912 		return TRUE;
1913 	}
1914 	else
1915 		map->map_mflags &= ~MF_IMPL_NDBM;
1916 #endif
1917 
1918 #if defined(NEWDB) || defined(NDBM)
1919 	if (Verbose)
1920 		message("WARNING: cannot open alias database %s", map->map_file);
1921 #else
1922 	if (mode != O_RDONLY)
1923 		usrerr("Cannot rebuild aliases: no database format defined");
1924 #endif
1925 
1926 	return stab_map_open(map, mode);
1927 }
1928 
1929 
1930 /*
1931 **  IMPL_MAP_CLOSE -- close any open database(s)
1932 */
1933 
1934 void
1935 impl_map_close(map)
1936 	MAP *map;
1937 {
1938 	if (tTd(38, 20))
1939 		printf("impl_map_close(%s, %s, %x)\n",
1940 			map->map_mname, map->map_file, map->map_mflags);
1941 #ifdef NEWDB
1942 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1943 	{
1944 		db_map_close(map);
1945 		map->map_mflags &= ~MF_IMPL_HASH;
1946 	}
1947 #endif
1948 
1949 #ifdef NDBM
1950 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1951 	{
1952 		ndbm_map_close(map);
1953 		map->map_mflags &= ~MF_IMPL_NDBM;
1954 	}
1955 #endif
1956 }
1957 /*
1958 **  User map class.
1959 **
1960 **	Provides access to the system password file.
1961 */
1962 
1963 /*
1964 **  USER_MAP_OPEN -- open user map
1965 **
1966 **	Really just binds field names to field numbers.
1967 */
1968 
1969 bool
1970 user_map_open(map, mode)
1971 	MAP *map;
1972 	int mode;
1973 {
1974 	if (tTd(38, 2))
1975 		printf("user_map_open(%s)\n", map->map_mname);
1976 
1977 	if (mode != O_RDONLY)
1978 	{
1979 		/* issue a pseudo-error message */
1980 #ifdef ENOSYS
1981 		errno = ENOSYS;
1982 #else
1983 # ifdef EFTYPE
1984 		errno = EFTYPE;
1985 # else
1986 		errno = ENXIO;
1987 # endif
1988 #endif
1989 		return FALSE;
1990 	}
1991 	if (map->map_valcolnm == NULL)
1992 		/* nothing */ ;
1993 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
1994 		map->map_valcolno = 1;
1995 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
1996 		map->map_valcolno = 2;
1997 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
1998 		map->map_valcolno = 3;
1999 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
2000 		map->map_valcolno = 4;
2001 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
2002 		map->map_valcolno = 5;
2003 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2004 		map->map_valcolno = 6;
2005 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2006 		map->map_valcolno = 7;
2007 	else
2008 	{
2009 		syserr("User map %s: unknown column name %s",
2010 			map->map_mname, map->map_valcolnm);
2011 		return FALSE;
2012 	}
2013 	return TRUE;
2014 }
2015 
2016 
2017 /*
2018 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2019 */
2020 
2021 char *
2022 user_map_lookup(map, key, av, statp)
2023 	MAP *map;
2024 	char *key;
2025 	char **av;
2026 	int *statp;
2027 {
2028 	struct passwd *pw;
2029 
2030 	if (tTd(38, 20))
2031 		printf("user_map_lookup(%s, %s)\n",
2032 			map->map_mname, key);
2033 
2034 	pw = sm_getpwnam(key);
2035 	if (pw == NULL)
2036 		return NULL;
2037 	if (bitset(MF_MATCHONLY, map->map_mflags))
2038 		return map_rewrite(map, key, strlen(key), NULL);
2039 	else
2040 	{
2041 		char *rwval = NULL;
2042 		char buf[30];
2043 
2044 		switch (map->map_valcolno)
2045 		{
2046 		  case 0:
2047 		  case 1:
2048 			rwval = pw->pw_name;
2049 			break;
2050 
2051 		  case 2:
2052 			rwval = pw->pw_passwd;
2053 			break;
2054 
2055 		  case 3:
2056 			sprintf(buf, "%d", pw->pw_uid);
2057 			rwval = buf;
2058 			break;
2059 
2060 		  case 4:
2061 			sprintf(buf, "%d", pw->pw_gid);
2062 			rwval = buf;
2063 			break;
2064 
2065 		  case 5:
2066 			rwval = pw->pw_gecos;
2067 			break;
2068 
2069 		  case 6:
2070 			rwval = pw->pw_dir;
2071 			break;
2072 
2073 		  case 7:
2074 			rwval = pw->pw_shell;
2075 			break;
2076 		}
2077 		return map_rewrite(map, rwval, strlen(rwval), av);
2078 	}
2079 }
2080 /*
2081 **  BESTMX -- find the best MX for a name
2082 **
2083 **	This is really a hack, but I don't see any obvious way
2084 **	to generalize it at the moment.
2085 */
2086 
2087 #if NAMED_BIND
2088 
2089 char *
2090 bestmx_map_lookup(map, name, av, statp)
2091 	MAP *map;
2092 	char *name;
2093 	char **av;
2094 	int *statp;
2095 {
2096         int nmx;
2097         auto int rcode;
2098         char *mxhosts[MAXMXHOSTS + 1];
2099 
2100 	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
2101 	if (nmx <= 0)
2102 		return NULL;
2103 	if (bitset(MF_MATCHONLY, map->map_mflags))
2104 		return map_rewrite(map, name, strlen(name), NULL);
2105 	else
2106 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
2107 }
2108 
2109 #endif
2110 /*
2111 **  Sequenced map type.
2112 **
2113 **	Tries each map in order until something matches, much like
2114 **	implicit.  Stores go to the first map in the list that can
2115 **	support storing.
2116 **
2117 **	This is slightly unusual in that there are two interfaces.
2118 **	The "sequence" interface lets you stack maps arbitrarily.
2119 **	The "switch" interface builds a sequence map by looking
2120 **	at a system-dependent configuration file such as
2121 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2122 **
2123 **	We don't need an explicit open, since all maps are
2124 **	opened during startup, including underlying maps.
2125 */
2126 
2127 /*
2128 **  SEQ_MAP_PARSE -- Sequenced map parsing
2129 */
2130 
2131 bool
2132 seq_map_parse(map, ap)
2133 	MAP *map;
2134 	char *ap;
2135 {
2136 	int maxmap;
2137 
2138 	if (tTd(38, 2))
2139 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2140 	maxmap = 0;
2141 	while (*ap != '\0')
2142 	{
2143 		register char *p;
2144 		STAB *s;
2145 
2146 		/* find beginning of map name */
2147 		while (isascii(*ap) && isspace(*ap))
2148 			ap++;
2149 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2150 			continue;
2151 		if (*p != '\0')
2152 			*p++ = '\0';
2153 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2154 			p++;
2155 		if (*ap == '\0')
2156 		{
2157 			ap = p;
2158 			continue;
2159 		}
2160 		s = stab(ap, ST_MAP, ST_FIND);
2161 		if (s == NULL)
2162 		{
2163 			syserr("Sequence map %s: unknown member map %s",
2164 				map->map_mname, ap);
2165 		}
2166 		else if (maxmap == MAXMAPSTACK)
2167 		{
2168 			syserr("Sequence map %s: too many member maps (%d max)",
2169 				map->map_mname, MAXMAPSTACK);
2170 			maxmap++;
2171 		}
2172 		else if (maxmap < MAXMAPSTACK)
2173 		{
2174 			map->map_stack[maxmap++] = &s->s_map;
2175 		}
2176 		ap = p;
2177 	}
2178 	return TRUE;
2179 }
2180 
2181 
2182 /*
2183 **  SWITCH_MAP_OPEN -- open a switched map
2184 **
2185 **	This looks at the system-dependent configuration and builds
2186 **	a sequence map that does the same thing.
2187 **
2188 **	Every system must define a switch_map_find routine in conf.c
2189 **	that will return the list of service types associated with a
2190 **	given service class.
2191 */
2192 
2193 bool
2194 switch_map_open(map, mode)
2195 	MAP *map;
2196 	int mode;
2197 {
2198 	int mapno;
2199 	int nmaps;
2200 	char *maptype[MAXMAPSTACK];
2201 
2202 	if (tTd(38, 2))
2203 		printf("switch_map_open(%s, %s, %d)\n",
2204 			map->map_mname, map->map_file, mode);
2205 
2206 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2207 	if (tTd(38, 19))
2208 	{
2209 		printf("\tswitch_map_find => %d\n", nmaps);
2210 		for (mapno = 0; mapno < nmaps; mapno++)
2211 			printf("\t\t%s\n", maptype[mapno]);
2212 	}
2213 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2214 		return FALSE;
2215 
2216 	for (mapno = 0; mapno < nmaps; mapno++)
2217 	{
2218 		register STAB *s;
2219 		char nbuf[MAXNAME + 1];
2220 
2221 		if (maptype[mapno] == NULL)
2222 			continue;
2223 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2224 		s = stab(nbuf, ST_MAP, ST_FIND);
2225 		if (s == NULL)
2226 		{
2227 			syserr("Switch map %s: unknown member map %s",
2228 				map->map_mname, nbuf);
2229 		}
2230 		else
2231 		{
2232 			map->map_stack[mapno] = &s->s_map;
2233 			if (tTd(38, 4))
2234 				printf("\tmap_stack[%d] = %s:%s\n",
2235 					mapno, s->s_map.map_class->map_cname,
2236 					nbuf);
2237 		}
2238 	}
2239 	return TRUE;
2240 }
2241 
2242 
2243 /*
2244 **  SEQ_MAP_CLOSE -- close all underlying maps
2245 */
2246 
2247 seq_map_close(map)
2248 	MAP *map;
2249 {
2250 	int mapno;
2251 
2252 	if (tTd(38, 20))
2253 		printf("seq_map_close(%s)\n", map->map_mname);
2254 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2255 	{
2256 		MAP *mm = map->map_stack[mapno];
2257 
2258 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2259 			continue;
2260 		mm->map_class->map_close(mm);
2261 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
2262 	}
2263 }
2264 
2265 
2266 /*
2267 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2268 */
2269 
2270 char *
2271 seq_map_lookup(map, key, args, pstat)
2272 	MAP *map;
2273 	char *key;
2274 	char **args;
2275 	int *pstat;
2276 {
2277 	int mapno;
2278 	int mapbit = 0x01;
2279 
2280 	if (tTd(38, 20))
2281 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2282 
2283 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2284 	{
2285 		MAP *mm = map->map_stack[mapno];
2286 		int stat = 0;
2287 		char *rv;
2288 
2289 		if (mm == NULL)
2290 			continue;
2291 		if (!bitset(MF_OPEN, mm->map_mflags))
2292 		{
2293 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2294 			{
2295 				*pstat = EX_UNAVAILABLE;
2296 				return NULL;
2297 			}
2298 			continue;
2299 		}
2300 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2301 		if (rv != NULL)
2302 			return rv;
2303 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2304 			return NULL;
2305 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2306 		{
2307 			*pstat = stat;
2308 			return NULL;
2309 		}
2310 	}
2311 	return NULL;
2312 }
2313 
2314 
2315 /*
2316 **  SEQ_MAP_STORE -- sequenced map store
2317 */
2318 
2319 void
2320 seq_map_store(map, key, val)
2321 	MAP *map;
2322 	char *key;
2323 	char *val;
2324 {
2325 	int mapno;
2326 
2327 	if (tTd(38, 12))
2328 		printf("seq_map_store(%s, %s, %s)\n",
2329 			map->map_mname, key, val);
2330 
2331 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2332 	{
2333 		MAP *mm = map->map_stack[mapno];
2334 
2335 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2336 			continue;
2337 
2338 		mm->map_class->map_store(mm, key, val);
2339 		return;
2340 	}
2341 	syserr("seq_map_store(%s, %s, %s): no writable map",
2342 		map->map_mname, key, val);
2343 }
2344 /*
2345 **  NULL stubs
2346 */
2347 
2348 bool
2349 null_map_open(map, mode)
2350 	MAP *map;
2351 	int mode;
2352 {
2353 	return TRUE;
2354 }
2355 
2356 void
2357 null_map_close(map)
2358 	MAP *map;
2359 {
2360 	return;
2361 }
2362 
2363 void
2364 null_map_store(map, key, val)
2365 	MAP *map;
2366 	char *key;
2367 	char *val;
2368 {
2369 	return;
2370 }
2371 
2372 
2373 /*
2374 **  BOGUS stubs
2375 */
2376 
2377 char *
2378 bogus_map_lookup(map, key, args, pstat)
2379 	MAP *map;
2380 	char *key;
2381 	char **args;
2382 	int *pstat;
2383 {
2384 	*pstat = EX_TEMPFAIL;
2385 	return NULL;
2386 }
2387 
2388 MAPCLASS	BogusMapClass =
2389 {
2390 	"bogus-map",		NULL,		0,
2391 	NULL,		bogus_map_lookup,	null_map_store,
2392 	null_map_open,	null_map_close,
2393 };
2394