xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision 3ac1c4b3)
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.50 (Berkeley) 03/14/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 			datum old;
601 
602 			old.dptr = ndbm_map_lookup(map, key.dptr, NULL);
603 			if (old.dptr != NULL && *old.dptr != '\0')
604 			{
605 				old.dsize = strlen(old.dptr);
606 				if (data.dsize + old.dsize + 2 > bufsiz)
607 				{
608 					if (buf != NULL)
609 						(void) free(buf);
610 					bufsiz = data.dsize + old.dsize + 2;
611 					buf = xalloc(bufsiz);
612 				}
613 				sprintf(buf, "%s,%s", data.dptr, old.dptr);
614 				data.dsize = data.dsize + old.dsize + 1;
615 				data.dptr = buf;
616 				if (tTd(38, 9))
617 					printf("ndbm_map_store append=%s\n", data.dptr);
618 			}
619 		}
620 		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
621 	}
622 	if (stat != 0)
623 		syserr("readaliases: dbm put (%s)", lhs);
624 }
625 
626 
627 /*
628 **  NDBM_MAP_CLOSE -- close the database
629 */
630 
631 void
632 ndbm_map_close(map)
633 	register MAP  *map;
634 {
635 	if (tTd(38, 9))
636 		printf("ndbm_map_close(%s, %s, %x)\n",
637 			map->map_mname, map->map_file, map->map_mflags);
638 
639 	if (bitset(MF_WRITABLE, map->map_mflags))
640 	{
641 #ifdef NIS
642 		bool inclnull;
643 		char buf[200];
644 
645 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
646 		map->map_mflags &= ~MF_INCLNULL;
647 
648 		(void) sprintf(buf, "%010ld", curtime());
649 		ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
650 
651 		(void) gethostname(buf, sizeof buf);
652 		ndbm_map_store(map, "YP_MASTER_NAME", buf);
653 
654 		if (inclnull)
655 			map->map_mflags |= MF_INCLNULL;
656 #endif
657 
658 		/* write out the distinguished alias */
659 		ndbm_map_store(map, "@", "@");
660 	}
661 	dbm_close((DBM *) map->map_db1);
662 }
663 
664 #endif
665 /*
666 **  NEWDB (Hash and BTree) Modules
667 */
668 
669 #ifdef NEWDB
670 
671 /*
672 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
673 **
674 **	These do rather bizarre locking.  If you can lock on open,
675 **	do that to avoid the condition of opening a database that
676 **	is being rebuilt.  If you don't, we'll try to fake it, but
677 **	there will be a race condition.  If opening for read-only,
678 **	we immediately release the lock to avoid freezing things up.
679 **	We really ought to hold the lock, but guarantee that we won't
680 **	be pokey about it.  That's hard to do.
681 */
682 
683 bool
684 bt_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 + 1];
694 
695 	if (tTd(38, 2))
696 		printf("bt_map_open(%s, %s, %d)\n",
697 			map->map_mname, map->map_file, mode);
698 
699 	omode = mode;
700 	if (omode == O_RDWR)
701 	{
702 		omode |= O_CREAT|O_TRUNC;
703 #if defined(O_EXLOCK) && HASFLOCK
704 		omode |= O_EXLOCK;
705 # if !OLD_NEWDB
706 	}
707 	else
708 	{
709 		omode |= O_SHLOCK;
710 # endif
711 #endif
712 	}
713 
714 	(void) strcpy(buf, map->map_file);
715 	i = strlen(buf);
716 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
717 		(void) strcat(buf, ".db");
718 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
719 	if (db == NULL)
720 	{
721 #ifdef MAYBENEXTRELEASE
722 		if (aliaswait(map, ".db", FALSE))
723 			return TRUE;
724 #endif
725 		if (!bitset(MF_OPTIONAL, map->map_mflags))
726 			syserr("Cannot open BTREE database %s", map->map_file);
727 		return FALSE;
728 	}
729 #if !OLD_NEWDB
730 	fd = db->fd(db);
731 # if HASFLOCK
732 #  if !defined(O_EXLOCK)
733 	if (mode == O_RDWR && fd >= 0)
734 	{
735 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
736 			map->map_mflags |= MF_LOCKED;
737 	}
738 #  else
739 	if (mode == O_RDONLY && fd >= 0)
740 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
741 	else
742 		map->map_mflags |= MF_LOCKED;
743 #  endif
744 # endif
745 #endif
746 
747 	/* try to make sure that at least the database header is on disk */
748 	if (mode == O_RDWR)
749 #if OLD_NEWDB
750 		(void) db->sync(db);
751 #else
752 		(void) db->sync(db, 0);
753 
754 	if (fd >= 0 && fstat(fd, &st) >= 0)
755 		map->map_mtime = st.st_mtime;
756 #endif
757 
758 	map->map_db2 = (void *) db;
759 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
760 		if (!aliaswait(map, ".db", TRUE))
761 			return FALSE;
762 	return TRUE;
763 }
764 
765 
766 /*
767 **  HASH_MAP_INIT -- HASH-style map initialization
768 */
769 
770 bool
771 hash_map_open(map, mode)
772 	MAP *map;
773 	int mode;
774 {
775 	DB *db;
776 	int i;
777 	int omode;
778 	int fd;
779 	struct stat st;
780 	char buf[MAXNAME + 1];
781 
782 	if (tTd(38, 2))
783 		printf("hash_map_open(%s, %s, %d)\n",
784 			map->map_mname, map->map_file, mode);
785 
786 	omode = mode;
787 	if (omode == O_RDWR)
788 	{
789 		omode |= O_CREAT|O_TRUNC;
790 #if defined(O_EXLOCK) && HASFLOCK
791 		omode |= O_EXLOCK;
792 # if !OLD_NEWDB
793 	}
794 	else
795 	{
796 		omode |= O_SHLOCK;
797 # endif
798 #endif
799 	}
800 
801 	(void) strcpy(buf, map->map_file);
802 	i = strlen(buf);
803 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
804 		(void) strcat(buf, ".db");
805 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
806 	if (db == NULL)
807 	{
808 #ifdef MAYBENEXTRELEASE
809 		if (aliaswait(map, ".db", FALSE))
810 			return TRUE;
811 #endif
812 		if (!bitset(MF_OPTIONAL, map->map_mflags))
813 			syserr("Cannot open HASH database %s", map->map_file);
814 		return FALSE;
815 	}
816 #if !OLD_NEWDB
817 	fd = db->fd(db);
818 # if HASFLOCK
819 #  if !defined(O_EXLOCK)
820 	if (mode == O_RDWR && fd >= 0)
821 	{
822 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
823 			map->map_mflags |= MF_LOCKED;
824 	}
825 #  else
826 	if (mode == O_RDONLY && fd >= 0)
827 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
828 	else
829 		map->map_mflags |= MF_LOCKED;
830 #  endif
831 # endif
832 #endif
833 
834 	/* try to make sure that at least the database header is on disk */
835 	if (mode == O_RDWR)
836 #if OLD_NEWDB
837 		(void) db->sync(db);
838 #else
839 		(void) db->sync(db, 0);
840 
841 	if (fd >= 0 && fstat(fd, &st) >= 0)
842 		map->map_mtime = st.st_mtime;
843 #endif
844 
845 	map->map_db2 = (void *) db;
846 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
847 		if (!aliaswait(map, ".db", TRUE))
848 			return FALSE;
849 	return TRUE;
850 }
851 
852 
853 /*
854 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
855 */
856 
857 char *
858 db_map_lookup(map, name, av, statp)
859 	MAP *map;
860 	char *name;
861 	char **av;
862 	int *statp;
863 {
864 	DBT key, val;
865 	register DB *db = (DB *) map->map_db2;
866 	int st;
867 	int saveerrno;
868 	int fd;
869 	char keybuf[MAXNAME + 1];
870 
871 	if (tTd(38, 20))
872 		printf("db_map_lookup(%s, %s)\n",
873 			map->map_mname, name);
874 
875 	key.size = strlen(name);
876 	if (key.size > sizeof keybuf - 1)
877 		key.size = sizeof keybuf - 1;
878 	key.data = keybuf;
879 	bcopy(name, keybuf, key.size + 1);
880 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
881 		makelower(keybuf);
882 #if !OLD_NEWDB
883 	fd = db->fd(db);
884 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
885 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
886 #endif
887 	st = 1;
888 	if (bitset(MF_TRY0NULL, map->map_mflags))
889 	{
890 		st = db->get(db, &key, &val, 0);
891 		if (st == 0)
892 			map->map_mflags &= ~MF_TRY1NULL;
893 	}
894 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
895 	{
896 		key.size++;
897 		st = db->get(db, &key, &val, 0);
898 		if (st == 0)
899 			map->map_mflags &= ~MF_TRY0NULL;
900 	}
901 	saveerrno = errno;
902 #if !OLD_NEWDB
903 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
904 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
905 #endif
906 	if (st != 0)
907 	{
908 		errno = saveerrno;
909 		if (st < 0)
910 			syserr("db_map_lookup: get (%s)", name);
911 		return NULL;
912 	}
913 	if (bitset(MF_MATCHONLY, map->map_mflags))
914 		return map_rewrite(map, name, strlen(name), NULL);
915 	else
916 		return map_rewrite(map, val.data, val.size, av);
917 }
918 
919 
920 /*
921 **  DB_MAP_STORE -- store a datum in the NEWDB database
922 */
923 
924 void
925 db_map_store(map, lhs, rhs)
926 	register MAP *map;
927 	char *lhs;
928 	char *rhs;
929 {
930 	int stat;
931 	DBT key;
932 	DBT data;
933 	register DB *db = map->map_db2;
934 
935 	if (tTd(38, 20))
936 		printf("db_map_store(%s, %s, %s)\n",
937 			map->map_mname, lhs, rhs);
938 
939 	key.size = strlen(lhs);
940 	key.data = lhs;
941 
942 	data.size = strlen(rhs);
943 	data.data = rhs;
944 
945 	if (bitset(MF_INCLNULL, map->map_mflags))
946 	{
947 		key.size++;
948 		data.size++;
949 	}
950 
951 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
952 	if (stat > 0)
953 	{
954 		if (!bitset(MF_APPEND, map->map_mflags))
955 			usrerr("050 Warning: duplicate alias name %s", lhs);
956 		else
957 		{
958 			static char *buf = NULL;
959 			static int bufsiz = 0;
960 			DBT old;
961 
962 			old.data = db_map_lookup(map, key.data, NULL, &stat);
963 			if (old.data != NULL)
964 			{
965 				old.size = strlen(old.data);
966 				if (data.size + old.size + 2 > bufsiz)
967 				{
968 					if (buf != NULL)
969 						(void) free(buf);
970 					bufsiz = data.size + old.size + 2;
971 					buf = xalloc(bufsiz);
972 				}
973 				sprintf(buf, "%s,%s", data.data, old.data);
974 				data.size = data.size + old.size + 1;
975 				data.data = buf;
976 				if (tTd(38, 9))
977 					printf("db_map_store append=%s\n", data.data);
978 			}
979 		}
980 		stat = db->put(db, &key, &data, 0);
981 	}
982 	if (stat != 0)
983 		syserr("readaliases: db put (%s)", lhs);
984 }
985 
986 
987 /*
988 **  DB_MAP_CLOSE -- add distinguished entries and close the database
989 */
990 
991 void
992 db_map_close(map)
993 	MAP *map;
994 {
995 	register DB *db = map->map_db2;
996 
997 	if (tTd(38, 9))
998 		printf("db_map_close(%s, %s, %x)\n",
999 			map->map_mname, map->map_file, map->map_mflags);
1000 
1001 	if (bitset(MF_WRITABLE, map->map_mflags))
1002 	{
1003 		/* write out the distinguished alias */
1004 		db_map_store(map, "@", "@");
1005 	}
1006 
1007 	if (db->close(db) != 0)
1008 		syserr("readaliases: db close failure");
1009 }
1010 
1011 #endif
1012 /*
1013 **  NIS Modules
1014 */
1015 
1016 # ifdef NIS
1017 
1018 # ifndef YPERR_BUSY
1019 #  define YPERR_BUSY	16
1020 # endif
1021 
1022 /*
1023 **  NIS_MAP_OPEN -- open DBM map
1024 */
1025 
1026 bool
1027 nis_map_open(map, mode)
1028 	MAP *map;
1029 	int mode;
1030 {
1031 	int yperr;
1032 	register char *p;
1033 	auto char *vp;
1034 	auto int vsize;
1035 	char *master;
1036 
1037 	if (tTd(38, 2))
1038 		printf("nis_map_open(%s, %s)\n",
1039 			map->map_mname, map->map_file);
1040 
1041 	if (mode != O_RDONLY)
1042 	{
1043 		/* issue a pseudo-error message */
1044 #ifdef ENOSYS
1045 		errno = ENOSYS;
1046 #else
1047 # ifdef EFTYPE
1048 		errno = EFTYPE;
1049 # else
1050 		errno = ENXIO;
1051 # endif
1052 #endif
1053 		return FALSE;
1054 	}
1055 
1056 	p = strchr(map->map_file, '@');
1057 	if (p != NULL)
1058 	{
1059 		*p++ = '\0';
1060 		if (*p != '\0')
1061 			map->map_domain = p;
1062 	}
1063 
1064 	if (*map->map_file == '\0')
1065 		map->map_file = "mail.aliases";
1066 
1067 	if (map->map_domain == NULL)
1068 	{
1069 		yperr = yp_get_default_domain(&map->map_domain);
1070 		if (yperr != 0)
1071 		{
1072 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1073 				syserr("421 NIS map %s specified, but NIS not running\n",
1074 					map->map_file);
1075 			return FALSE;
1076 		}
1077 	}
1078 
1079 	/* check to see if this map actually exists */
1080 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1081 			&vp, &vsize);
1082 	if (tTd(38, 10))
1083 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1084 			map->map_domain, map->map_file, yperr_string(yperr));
1085 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1086 	{
1087 		if (!bitset(MF_ALIAS, map->map_mflags) ||
1088 		    aliaswait(map, NULL, TRUE))
1089 			return TRUE;
1090 	}
1091 
1092 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1093 		syserr("421 Cannot bind to domain %s: %s", map->map_domain,
1094 			yperr_string(yperr));
1095 
1096 	return FALSE;
1097 }
1098 
1099 
1100 /*
1101 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1102 */
1103 
1104 char *
1105 nis_map_lookup(map, name, av, statp)
1106 	MAP *map;
1107 	char *name;
1108 	char **av;
1109 	int *statp;
1110 {
1111 	char *vp;
1112 	auto int vsize;
1113 	int buflen;
1114 	int yperr;
1115 	char keybuf[MAXNAME + 1];
1116 
1117 	if (tTd(38, 20))
1118 		printf("nis_map_lookup(%s, %s)\n",
1119 			map->map_mname, name);
1120 
1121 	buflen = strlen(name);
1122 	if (buflen > sizeof keybuf - 1)
1123 		buflen = sizeof keybuf - 1;
1124 	bcopy(name, keybuf, buflen + 1);
1125 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1126 		makelower(keybuf);
1127 	yperr = YPERR_KEY;
1128 	if (bitset(MF_TRY0NULL, map->map_mflags))
1129 	{
1130 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1131 			     &vp, &vsize);
1132 		if (yperr == 0)
1133 			map->map_mflags &= ~MF_TRY1NULL;
1134 	}
1135 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1136 	{
1137 		buflen++;
1138 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1139 			     &vp, &vsize);
1140 		if (yperr == 0)
1141 			map->map_mflags &= ~MF_TRY0NULL;
1142 	}
1143 	if (yperr != 0)
1144 	{
1145 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1146 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1147 		return NULL;
1148 	}
1149 	if (bitset(MF_MATCHONLY, map->map_mflags))
1150 		return map_rewrite(map, name, strlen(name), NULL);
1151 	else
1152 		return map_rewrite(map, vp, vsize, av);
1153 }
1154 
1155 #endif
1156 /*
1157 **  NISPLUS Modules
1158 **
1159 **	This code donated by Sun Microsystems.
1160 */
1161 
1162 #ifdef NISPLUS
1163 
1164 #undef NIS /* symbol conflict in nis.h */
1165 #include <rpcsvc/nis.h>
1166 #include <rpcsvc/nislib.h>
1167 
1168 #define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1169 #define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1170 #define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1171 #define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
1172 
1173 /*
1174 **  NISPLUS_MAP_OPEN -- open nisplus table
1175 */
1176 
1177 bool
1178 nisplus_map_open(map, mode)
1179 	MAP *map;
1180 	int mode;
1181 {
1182 	register char *p;
1183 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1184 	nis_result *res = NULL;
1185 	u_int objs_len;
1186 	nis_object *obj_ptr;
1187 	int retry_cnt, max_col, i;
1188 
1189 	if (tTd(38, 2))
1190 		printf("nisplus_map_open(%s, %s, %d)\n",
1191 			map->map_mname, map->map_file, mode);
1192 
1193 	if (mode != O_RDONLY)
1194 	{
1195 		errno = ENODEV;
1196 		return FALSE;
1197 	}
1198 
1199 	if (*map->map_file == '\0')
1200 		map->map_file = "mail_aliases.org_dir";
1201 
1202 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1203 	{
1204 		/* set default NISPLUS Domain to $m */
1205 		extern char *nisplus_default_domain();
1206 
1207 		map->map_domain = newstr(nisplus_default_domain());
1208 		if (tTd(38, 2))
1209 			printf("nisplus_map_open(%s): using domain %s\n",
1210 				 map->map_file, map->map_domain);
1211 	}
1212 	if (!PARTIAL_NAME(map->map_file))
1213 		map->map_domain = newstr("");
1214 
1215 	/* check to see if this map actually exists */
1216 	if (PARTIAL_NAME(map->map_file))
1217 		sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
1218 	else
1219 		strcpy(qbuf, map->map_file);
1220 
1221 	retry_cnt = 0;
1222 	while (res == NULL || res->status != NIS_SUCCESS)
1223 	{
1224 		res = nis_lookup(qbuf, FOLLOW_LINKS);
1225 		switch (res->status)
1226 		{
1227 		  case NIS_SUCCESS:
1228 		  case NIS_TRYAGAIN:
1229 		  case NIS_RPCERROR:
1230 		  case NIS_NAMEUNREACHABLE:
1231 			break;
1232 
1233 		  default:		/* all other nisplus errors */
1234 #if 0
1235 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1236 				syserr("421 Cannot find table %s.%s: %s",
1237 					map->map_file, map->map_domain,
1238 					nis_sperrno(res->status));
1239 #endif
1240 			errno = EBADR;
1241 			return FALSE;
1242 		}
1243 		sleep(2);		/* try not to overwhelm hosed server */
1244 		if (retry_cnt++ > 4)
1245 		{
1246 			errno = EBADR;
1247 			return FALSE;
1248 		}
1249 	}
1250 
1251 	if (NIS_RES_NUMOBJ(res) != 1 ||
1252 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1253 	{
1254 		if (tTd(38, 10))
1255 			printf("nisplus_map_open: %s is not a table\n", qbuf);
1256 #if 0
1257 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1258 			syserr("421 %s.%s: %s is not a table",
1259 				map->map_file, map->map_domain,
1260 				nis_sperrno(res->status));
1261 #endif
1262 		errno = EBADR;
1263 		return FALSE;
1264 	}
1265 	/* default key column is column 0 */
1266 	if (map->map_keycolnm == NULL)
1267 		map->map_keycolnm = newstr(COL_NAME(res,0));
1268 
1269 	max_col = COL_MAX(res);
1270 
1271 	/* verify the key column exist */
1272 	for (i=0; i< max_col; i++)
1273 	{
1274 		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
1275 			break;
1276 	}
1277 	if (i == max_col)
1278 	{
1279 		if (tTd(38, 2))
1280 			printf("nisplus_map_open(%s): can not find key column %s\n",
1281 				map->map_file, map->map_keycolnm);
1282 		errno = EBADR;
1283 		return FALSE;
1284 	}
1285 
1286 	/* default value column is the last column */
1287 	if (map->map_valcolnm == NULL)
1288 	{
1289 		map->map_valcolno = max_col - 1;
1290 		return TRUE;
1291 	}
1292 
1293 	for (i=0; i< max_col; i++)
1294 	{
1295 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1296 		{
1297 			map->map_valcolno = i;
1298 			return TRUE;
1299 		}
1300 	}
1301 
1302 	if (tTd(38, 2))
1303 		printf("nisplus_map_open(%s): can not find column %s\n",
1304 			 map->map_file, map->map_keycolnm);
1305 	errno = EBADR;
1306 	return FALSE;
1307 }
1308 
1309 
1310 /*
1311 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1312 */
1313 
1314 char *
1315 nisplus_map_lookup(map, name, av, statp)
1316 	MAP *map;
1317 	char *name;
1318 	char **av;
1319 	int *statp;
1320 {
1321 	char *vp;
1322 	auto int vsize;
1323 	int buflen;
1324 	char search_key[MAXNAME + 1];
1325 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1326 	nis_result *result;
1327 
1328 	if (tTd(38, 20))
1329 		printf("nisplus_map_lookup(%s, %s)\n",
1330 			map->map_mname, name);
1331 
1332 	if (!bitset(MF_OPEN, map->map_mflags))
1333 	{
1334 		if (nisplus_map_open(map, O_RDONLY))
1335 			map->map_mflags |= MF_OPEN;
1336 		else
1337 		{
1338 			*statp = EX_UNAVAILABLE;
1339 			return NULL;
1340 		}
1341 	}
1342 
1343 	buflen = strlen(name);
1344 	if (buflen > sizeof search_key - 1)
1345 		buflen = sizeof search_key - 1;
1346 	bcopy(name, search_key, buflen + 1);
1347 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1348 		makelower(search_key);
1349 
1350 	/* construct the query */
1351 	if (PARTIAL_NAME(map->map_file))
1352 		sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
1353 			search_key, map->map_file, map->map_domain);
1354 	else
1355 		sprintf(qbuf, "[%s=%s],%s", map->map_keycolnm,
1356 			search_key, map->map_file);
1357 
1358 	if (tTd(38, 20))
1359 		printf("qbuf=%s\n", qbuf);
1360 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1361 	if (result->status == NIS_SUCCESS)
1362 	{
1363 		int count;
1364 		char *str;
1365 
1366 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1367 		{
1368 			if (LogLevel > 10)
1369 				syslog(LOG_WARNING,
1370 				  "%s:Lookup error, expected 1 entry, got (%d)",
1371 				    map->map_file, count);
1372 
1373 			/* ignore second entry */
1374 			if (tTd(38, 20))
1375 				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1376 					name, count);
1377 		}
1378 
1379 		vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1380 		/* set the length of the result */
1381 		if (vp == NULL)
1382 			vp = "";
1383 		vsize = strlen(vp);
1384 		if (tTd(38, 20))
1385 			printf("nisplus_map_lookup(%s), found %s\n",
1386 				name, vp);
1387 		if (bitset(MF_MATCHONLY, map->map_mflags))
1388 			str = map_rewrite(map, name, strlen(name), NULL);
1389 		else
1390 			str = map_rewrite(map, vp, vsize, av);
1391 		nis_freeresult(result);
1392 #ifdef MAP_EXIT_STAT
1393 		*statp = EX_OK;
1394 #endif
1395 		return str;
1396 	}
1397 	else
1398 	{
1399 #ifdef MAP_EXIT_STAT
1400 		if (result->status == NIS_NOTFOUND)
1401 			*statp = EX_NOTFOUND;
1402 		else if (result->status == NIS_TRYAGAIN)
1403 			*statp = EX_TEMPFAIL;
1404 		else
1405 		{
1406 			*statp = EX_UNAVAILABLE;
1407 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1408 		}
1409 #else
1410 		if ((result->status != NIS_NOTFOUND) &&
1411 		    (result->status != NIS_TRYAGAIN))
1412 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1413 #endif
1414 	}
1415 	if (tTd(38, 20))
1416 		printf("nisplus_map_lookup(%s), failed\n", name);
1417 	nis_freeresult(result);
1418 	return NULL;
1419 }
1420 
1421 
1422 char *
1423 nisplus_default_domain()
1424 {
1425 	static char default_domain[MAXNAME + 1] = "";
1426 	char *p;
1427 
1428 	if (default_domain[0] != '\0')
1429 		return(default_domain);
1430 
1431 	p = nis_local_directory();
1432 	strcpy(default_domain, p);
1433 	return default_domain;
1434 }
1435 
1436 #endif /* NISPLUS */
1437 /*
1438 **  HESIOD Modules
1439 */
1440 
1441 #ifdef HESIOD
1442 
1443 #include <hesiod.h>
1444 
1445 char *
1446 hes_map_lookup(map, name, av, statp)
1447         MAP *map;
1448         char *name;
1449         char **av;
1450         int *statp;
1451 {
1452 	char **hp;
1453 	char *retdata = NULL;
1454 	int i;
1455 
1456 	if (tTd(38, 20))
1457 		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1458 
1459 	hp = hes_resolve(name, map->map_file);
1460 	if (hp == NULL)
1461 		return NULL;
1462 
1463 	if (hp[0] != NULL)
1464 	{
1465 		if (tTd(38, 20))
1466 			printf("  %d %s\n", i, hp[0]);
1467 		if (bitset(MF_MATCHONLY, map->map_mflags))
1468 			retdata = map_rewrite(map, name, strlen(name), NULL);
1469 		else
1470 			retdata = map_rewrite(map, hp[0], strlen(hp[0]), av);
1471 	}
1472 
1473 	for (i = 0; hp[i] != NULL; i++)
1474 		free(hp[i]);
1475 	free(hp);
1476 	return retdata;
1477 }
1478 
1479 #endif
1480 /*
1481 **  NeXT NETINFO Modules
1482 */
1483 
1484 #ifdef NETINFO
1485 
1486 #define NETINFO_DEFAULT_DIR		"/aliases"
1487 #define NETINFO_DEFAULT_PROPERTY	"members"
1488 
1489 
1490 /*
1491 **  NI_MAP_OPEN -- open NetInfo Aliases
1492 */
1493 
1494 bool
1495 ni_map_open(map, mode)
1496 	MAP *map;
1497 	int mode;
1498 {
1499 	char *p;
1500 
1501 	if (tTd(38, 20))
1502 		printf("ni_map_open: %s\n", map->map_file);
1503 
1504 	if (*map->map_file == '\0')
1505 		map->map_file = NETINFO_DEFAULT_DIR;
1506 
1507 	if (map->map_valcolnm == NULL)
1508 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1509 
1510 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1511 		map->map_coldelim = ',';
1512 
1513 	return TRUE;
1514 }
1515 
1516 
1517 /*
1518 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1519 */
1520 
1521 char *
1522 ni_map_lookup(map, name, av, statp)
1523 	MAP *map;
1524 	char *name;
1525 	char **av;
1526 	int *statp;
1527 {
1528 	char *res;
1529 	char *propval;
1530 	extern char *ni_propval();
1531 
1532 	if (tTd(38, 20))
1533 		printf("ni_map_lookup(%s, %s)\n",
1534 			map->map_mname, name);
1535 
1536 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1537 			     map->map_valcolnm, map->map_coldelim);
1538 
1539 	if (propval == NULL)
1540 		return NULL;
1541 
1542 	if (bitset(MF_MATCHONLY, map->map_mflags))
1543 		res = map_rewrite(map, name, strlen(name), NULL);
1544 	else
1545 		res = map_rewrite(map, propval, strlen(propval), av);
1546 	free(propval);
1547 	return res;
1548 }
1549 
1550 #endif
1551 /*
1552 **  TEXT (unindexed text file) Modules
1553 **
1554 **	This code donated by Sun Microsystems.
1555 */
1556 
1557 
1558 /*
1559 **  TEXT_MAP_OPEN -- open text table
1560 */
1561 
1562 bool
1563 text_map_open(map, mode)
1564 	MAP *map;
1565 	int mode;
1566 {
1567 	struct stat sbuf;
1568 
1569 	if (tTd(38, 2))
1570 		printf("text_map_open(%s, %s, %d)\n",
1571 			map->map_mname, map->map_file, mode);
1572 
1573 	if (mode != O_RDONLY)
1574 	{
1575 		errno = ENODEV;
1576 		return FALSE;
1577 	}
1578 
1579 	if (*map->map_file == '\0')
1580 	{
1581 		if (tTd(38, 2))
1582 			printf("text_map_open: file name required\n");
1583 		return FALSE;
1584 	}
1585 
1586 	if (map->map_file[0] != '/')
1587 	{
1588 		if (tTd(38, 2))
1589 			printf("text_map_open(%s): file name must be fully qualified\n",
1590 				map->map_file);
1591 		return FALSE;
1592 	}
1593 	/* check to see if this map actually accessable */
1594 	if (access(map->map_file, R_OK) <0)
1595 		return FALSE;
1596 
1597 	/* check to see if this map actually exist */
1598 	if (stat(map->map_file, &sbuf) <0)
1599 	{
1600 		if (tTd(38, 2))
1601 			printf("text_map_open(%s): can not stat %s\n",
1602 				map->map_file, map->map_file);
1603 		return FALSE;
1604 	}
1605 
1606 	if (!S_ISREG(sbuf.st_mode))
1607 	{
1608 		if (tTd(38, 2))
1609 			printf("text_map_open(%s): %s is not a file\n",
1610 				map->map_file, map->map_file);
1611 		return FALSE;
1612 	}
1613 
1614 	if (map->map_keycolnm == NULL)
1615 		map->map_keycolno = 0;
1616 	else
1617 	{
1618 		if (!isdigit(*map->map_keycolnm))
1619 		{
1620 			if (tTd(38, 2))
1621 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1622 					map->map_file, map->map_keycolnm);
1623 			return FALSE;
1624 		}
1625 		map->map_keycolno = atoi(map->map_keycolnm);
1626 	}
1627 
1628 	if (map->map_valcolnm == NULL)
1629 		map->map_valcolno = 0;
1630 	else
1631 	{
1632 		if (!isdigit(*map->map_valcolnm))
1633 		{
1634 			if (tTd(38, 2))
1635 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1636 					map->map_file, map->map_valcolnm);
1637 			return FALSE;
1638 		}
1639 		map->map_valcolno = atoi(map->map_valcolnm);
1640 	}
1641 
1642 	if (tTd(38, 2))
1643 	{
1644 		printf("text_map_open(%s): delimiter = ",
1645 			map->map_file);
1646 		if (map->map_coldelim == '\0')
1647 			printf("(white space)\n");
1648 		else
1649 			printf("%c\n", map->map_coldelim);
1650 	}
1651 
1652 	return TRUE;
1653 }
1654 
1655 
1656 /*
1657 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1658 */
1659 
1660 char *
1661 text_map_lookup(map, name, av, statp)
1662 	MAP *map;
1663 	char *name;
1664 	char **av;
1665 	int *statp;
1666 {
1667 	char *vp;
1668 	auto int vsize;
1669 	int buflen;
1670 	char search_key[MAXNAME + 1];
1671 	char linebuf[MAXLINE];
1672 	FILE *f;
1673 	char buf[MAXNAME + 1];
1674 	char delim;
1675 	int key_idx;
1676 	bool found_it;
1677 	extern char *get_column();
1678 
1679 
1680 	found_it = FALSE;
1681 	if (tTd(38, 20))
1682 		printf("text_map_lookup(%s)\n", name);
1683 
1684 	buflen = strlen(name);
1685 	if (buflen > sizeof search_key - 1)
1686 		buflen = sizeof search_key - 1;
1687 	bcopy(name, search_key, buflen + 1);
1688 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1689 		makelower(search_key);
1690 
1691 	f = fopen(map->map_file, "r");
1692 	if (f == NULL)
1693 	{
1694 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1695 		*statp = EX_UNAVAILABLE;
1696 		return NULL;
1697 	}
1698 	key_idx = map->map_keycolno;
1699 	delim = map->map_coldelim;
1700 	while (fgets(linebuf, MAXLINE, f))
1701 	{
1702 		char *lf;
1703 		if (linebuf[0] == '#')
1704 			continue; /* skip comment line */
1705 		if (lf = strchr(linebuf, '\n'))
1706 			*lf = '\0';
1707 		if (!strcasecmp(search_key,
1708 				get_column(linebuf, key_idx, delim, buf)))
1709 		{
1710 			found_it = TRUE;
1711 			break;
1712 		}
1713 	}
1714 	fclose(f);
1715 	if (!found_it)
1716 	{
1717 #ifdef MAP_EXIT_STAT
1718 		*statp = EX_NOTFOUND;
1719 #endif
1720 		return(NULL);
1721 	}
1722 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1723 	vsize = strlen(vp);
1724 #ifdef MAP_EXIT_STAT
1725 	*statp = EX_OK;
1726 #endif
1727 	if (bitset(MF_MATCHONLY, map->map_mflags))
1728 		return map_rewrite(map, name, strlen(name), NULL);
1729 	else
1730 		return map_rewrite(map, vp, vsize, av);
1731 }
1732 /*
1733 **  STAB (Symbol Table) Modules
1734 */
1735 
1736 
1737 /*
1738 **  STAB_MAP_LOOKUP -- look up alias in symbol table
1739 */
1740 
1741 char *
1742 stab_map_lookup(map, name, av, pstat)
1743 	register MAP *map;
1744 	char *name;
1745 	char **av;
1746 	int *pstat;
1747 {
1748 	register STAB *s;
1749 
1750 	if (tTd(38, 20))
1751 		printf("stab_lookup(%s, %s)\n",
1752 			map->map_mname, name);
1753 
1754 	s = stab(name, ST_ALIAS, ST_FIND);
1755 	if (s != NULL)
1756 		return (s->s_alias);
1757 	return (NULL);
1758 }
1759 
1760 
1761 /*
1762 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1763 */
1764 
1765 void
1766 stab_map_store(map, lhs, rhs)
1767 	register MAP *map;
1768 	char *lhs;
1769 	char *rhs;
1770 {
1771 	register STAB *s;
1772 
1773 	s = stab(lhs, ST_ALIAS, ST_ENTER);
1774 	s->s_alias = newstr(rhs);
1775 }
1776 
1777 
1778 /*
1779 **  STAB_MAP_OPEN -- initialize (reads data file)
1780 **
1781 **	This is a wierd case -- it is only intended as a fallback for
1782 **	aliases.  For this reason, opens for write (only during a
1783 **	"newaliases") always fails, and opens for read open the
1784 **	actual underlying text file instead of the database.
1785 */
1786 
1787 bool
1788 stab_map_open(map, mode)
1789 	register MAP *map;
1790 	int mode;
1791 {
1792 	FILE *af;
1793 	struct stat st;
1794 
1795 	if (tTd(38, 2))
1796 		printf("stab_map_open(%s, %s)\n",
1797 			map->map_mname, map->map_file);
1798 
1799 	if (mode != O_RDONLY)
1800 	{
1801 		errno = ENODEV;
1802 		return FALSE;
1803 	}
1804 
1805 	af = fopen(map->map_file, "r");
1806 	if (af == NULL)
1807 		return FALSE;
1808 	readaliases(map, af, FALSE, FALSE);
1809 
1810 	if (fstat(fileno(af), &st) >= 0)
1811 		map->map_mtime = st.st_mtime;
1812 	fclose(af);
1813 
1814 	return TRUE;
1815 }
1816 /*
1817 **  Implicit Modules
1818 **
1819 **	Tries several types.  For back compatibility of aliases.
1820 */
1821 
1822 
1823 /*
1824 **  IMPL_MAP_LOOKUP -- lookup in best open database
1825 */
1826 
1827 char *
1828 impl_map_lookup(map, name, av, pstat)
1829 	MAP *map;
1830 	char *name;
1831 	char **av;
1832 	int *pstat;
1833 {
1834 	if (tTd(38, 20))
1835 		printf("impl_map_lookup(%s, %s)\n",
1836 			map->map_mname, name);
1837 
1838 #ifdef NEWDB
1839 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1840 		return db_map_lookup(map, name, av, pstat);
1841 #endif
1842 #ifdef NDBM
1843 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1844 		return ndbm_map_lookup(map, name, av, pstat);
1845 #endif
1846 	return stab_map_lookup(map, name, av, pstat);
1847 }
1848 
1849 /*
1850 **  IMPL_MAP_STORE -- store in open databases
1851 */
1852 
1853 void
1854 impl_map_store(map, lhs, rhs)
1855 	MAP *map;
1856 	char *lhs;
1857 	char *rhs;
1858 {
1859 #ifdef NEWDB
1860 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1861 		db_map_store(map, lhs, rhs);
1862 #endif
1863 #ifdef NDBM
1864 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1865 		ndbm_map_store(map, lhs, rhs);
1866 #endif
1867 	stab_map_store(map, lhs, rhs);
1868 }
1869 
1870 /*
1871 **  IMPL_MAP_OPEN -- implicit database open
1872 */
1873 
1874 bool
1875 impl_map_open(map, mode)
1876 	MAP *map;
1877 	int mode;
1878 {
1879 	struct stat stb;
1880 
1881 	if (tTd(38, 2))
1882 		printf("impl_map_open(%s, %s, %d)\n",
1883 			map->map_mname, map->map_file, mode);
1884 
1885 	if (stat(map->map_file, &stb) < 0)
1886 	{
1887 		/* no alias file at all */
1888 		if (tTd(38, 3))
1889 			printf("no map file\n");
1890 		return FALSE;
1891 	}
1892 
1893 #ifdef NEWDB
1894 	map->map_mflags |= MF_IMPL_HASH;
1895 	if (hash_map_open(map, mode))
1896 	{
1897 #if defined(NDBM) && defined(NIS)
1898 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
1899 #endif
1900 			return TRUE;
1901 	}
1902 	else
1903 		map->map_mflags &= ~MF_IMPL_HASH;
1904 #endif
1905 #ifdef NDBM
1906 	map->map_mflags |= MF_IMPL_NDBM;
1907 	if (ndbm_map_open(map, mode))
1908 	{
1909 		return TRUE;
1910 	}
1911 	else
1912 		map->map_mflags &= ~MF_IMPL_NDBM;
1913 #endif
1914 
1915 #if defined(NEWDB) || defined(NDBM)
1916 	if (Verbose)
1917 		message("WARNING: cannot open alias database %s", map->map_file);
1918 #else
1919 	if (mode != O_RDONLY)
1920 		usrerr("Cannot rebuild aliases: no database format defined");
1921 #endif
1922 
1923 	return stab_map_open(map, mode);
1924 }
1925 
1926 
1927 /*
1928 **  IMPL_MAP_CLOSE -- close any open database(s)
1929 */
1930 
1931 void
1932 impl_map_close(map)
1933 	MAP *map;
1934 {
1935 	if (tTd(38, 20))
1936 		printf("impl_map_close(%s, %s, %x)\n",
1937 			map->map_mname, map->map_file, map->map_mflags);
1938 #ifdef NEWDB
1939 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1940 	{
1941 		db_map_close(map);
1942 		map->map_mflags &= ~MF_IMPL_HASH;
1943 	}
1944 #endif
1945 
1946 #ifdef NDBM
1947 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1948 	{
1949 		ndbm_map_close(map);
1950 		map->map_mflags &= ~MF_IMPL_NDBM;
1951 	}
1952 #endif
1953 }
1954 /*
1955 **  User map class.
1956 **
1957 **	Provides access to the system password file.
1958 */
1959 
1960 /*
1961 **  USER_MAP_OPEN -- open user map
1962 **
1963 **	Really just binds field names to field numbers.
1964 */
1965 
1966 bool
1967 user_map_open(map, mode)
1968 	MAP *map;
1969 	int mode;
1970 {
1971 	if (tTd(38, 2))
1972 		printf("user_map_open(%s)\n", map->map_mname);
1973 
1974 	if (mode != O_RDONLY)
1975 	{
1976 		/* issue a pseudo-error message */
1977 #ifdef ENOSYS
1978 		errno = ENOSYS;
1979 #else
1980 # ifdef EFTYPE
1981 		errno = EFTYPE;
1982 # else
1983 		errno = ENXIO;
1984 # endif
1985 #endif
1986 		return FALSE;
1987 	}
1988 	if (map->map_valcolnm == NULL)
1989 		/* nothing */ ;
1990 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
1991 		map->map_valcolno = 1;
1992 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
1993 		map->map_valcolno = 2;
1994 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
1995 		map->map_valcolno = 3;
1996 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
1997 		map->map_valcolno = 4;
1998 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
1999 		map->map_valcolno = 5;
2000 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2001 		map->map_valcolno = 6;
2002 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2003 		map->map_valcolno = 7;
2004 	else
2005 	{
2006 		syserr("User map %s: unknown column name %s",
2007 			map->map_mname, map->map_valcolnm);
2008 		return FALSE;
2009 	}
2010 	return TRUE;
2011 }
2012 
2013 
2014 /*
2015 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2016 */
2017 
2018 #include <pwd.h>
2019 
2020 char *
2021 user_map_lookup(map, key, av, statp)
2022 	MAP *map;
2023 	char *key;
2024 	char **av;
2025 	int *statp;
2026 {
2027 	struct passwd *pw;
2028 
2029 	if (tTd(38, 20))
2030 		printf("user_map_lookup(%s, %s)\n",
2031 			map->map_mname, key);
2032 
2033 	pw = getpwnam(key);
2034 	if (pw == NULL)
2035 		return NULL;
2036 	if (bitset(MF_MATCHONLY, map->map_mflags))
2037 		return map_rewrite(map, key, strlen(key), NULL);
2038 	else
2039 	{
2040 		char *rwval = NULL;
2041 		char buf[30];
2042 
2043 		switch (map->map_valcolno)
2044 		{
2045 		  case 0:
2046 		  case 1:
2047 			rwval = pw->pw_name;
2048 			break;
2049 
2050 		  case 2:
2051 			rwval = pw->pw_passwd;
2052 			break;
2053 
2054 		  case 3:
2055 			sprintf(buf, "%d", pw->pw_uid);
2056 			rwval = buf;
2057 			break;
2058 
2059 		  case 4:
2060 			sprintf(buf, "%d", pw->pw_gid);
2061 			rwval = buf;
2062 			break;
2063 
2064 		  case 5:
2065 			rwval = pw->pw_gecos;
2066 			break;
2067 
2068 		  case 6:
2069 			rwval = pw->pw_dir;
2070 			break;
2071 
2072 		  case 7:
2073 			rwval = pw->pw_shell;
2074 			break;
2075 		}
2076 		return map_rewrite(map, rwval, strlen(rwval), av);
2077 	}
2078 }
2079 /*
2080 **  BESTMX -- find the best MX for a name
2081 **
2082 **	This is really a hack, but I don't see any obvious way
2083 **	to generalize it at the moment.
2084 */
2085 
2086 #if NAMED_BIND
2087 
2088 char *
2089 bestmx_map_lookup(map, name, av, statp)
2090 	MAP *map;
2091 	char *name;
2092 	char **av;
2093 	int *statp;
2094 {
2095         int nmx;
2096         auto int rcode;
2097         char *mxhosts[MAXMXHOSTS + 1];
2098 
2099 	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
2100 	if (nmx <= 0)
2101 		return NULL;
2102 	if (bitset(MF_MATCHONLY, map->map_mflags))
2103 		return map_rewrite(map, name, strlen(name), NULL);
2104 	else
2105 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
2106 }
2107 
2108 #endif
2109 /*
2110 **  Sequenced map type.
2111 **
2112 **	Tries each map in order until something matches, much like
2113 **	implicit.  Stores go to the first map in the list that can
2114 **	support storing.
2115 **
2116 **	This is slightly unusual in that there are two interfaces.
2117 **	The "sequence" interface lets you stack maps arbitrarily.
2118 **	The "switch" interface builds a sequence map by looking
2119 **	at a system-dependent configuration file such as
2120 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2121 **
2122 **	We don't need an explicit open, since all maps are
2123 **	opened during startup, including underlying maps.
2124 */
2125 
2126 /*
2127 **  SEQ_MAP_PARSE -- Sequenced map parsing
2128 */
2129 
2130 bool
2131 seq_map_parse(map, ap)
2132 	MAP *map;
2133 	char *ap;
2134 {
2135 	int maxmap;
2136 
2137 	if (tTd(38, 2))
2138 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2139 	maxmap = 0;
2140 	while (*ap != '\0')
2141 	{
2142 		register char *p;
2143 		STAB *s;
2144 
2145 		/* find beginning of map name */
2146 		while (isascii(*ap) && isspace(*ap))
2147 			ap++;
2148 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2149 			continue;
2150 		if (*p != '\0')
2151 			*p++ = '\0';
2152 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2153 			p++;
2154 		if (*ap == '\0')
2155 		{
2156 			ap = p;
2157 			continue;
2158 		}
2159 		s = stab(ap, ST_MAP, ST_FIND);
2160 		if (s == NULL)
2161 		{
2162 			syserr("Sequence map %s: unknown member map %s",
2163 				map->map_mname, ap);
2164 		}
2165 		else if (maxmap == MAXMAPSTACK)
2166 		{
2167 			syserr("Sequence map %s: too many member maps (%d max)",
2168 				map->map_mname, MAXMAPSTACK);
2169 			maxmap++;
2170 		}
2171 		else if (maxmap < MAXMAPSTACK)
2172 		{
2173 			map->map_stack[maxmap++] = &s->s_map;
2174 		}
2175 		ap = p;
2176 	}
2177 	return TRUE;
2178 }
2179 
2180 
2181 /*
2182 **  SWITCH_MAP_OPEN -- open a switched map
2183 **
2184 **	This looks at the system-dependent configuration and builds
2185 **	a sequence map that does the same thing.
2186 **
2187 **	Every system must define a switch_map_find routine in conf.c
2188 **	that will return the list of service types associated with a
2189 **	given service class.
2190 */
2191 
2192 bool
2193 switch_map_open(map, mode)
2194 	MAP *map;
2195 	int mode;
2196 {
2197 	int mapno;
2198 	int nmaps;
2199 	char *maptype[MAXMAPSTACK];
2200 
2201 	if (tTd(38, 2))
2202 		printf("switch_map_open(%s, %s, %d)\n",
2203 			map->map_mname, map->map_file, mode);
2204 
2205 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2206 	if (tTd(38, 19))
2207 	{
2208 		printf("\tswitch_map_find => %d\n", nmaps);
2209 		for (mapno = 0; mapno < nmaps; mapno++)
2210 			printf("\t\t%s\n", maptype[mapno]);
2211 	}
2212 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2213 		return FALSE;
2214 
2215 	for (mapno = 0; mapno < nmaps; mapno++)
2216 	{
2217 		register STAB *s;
2218 		char nbuf[MAXNAME + 1];
2219 
2220 		if (maptype[mapno] == NULL)
2221 			continue;
2222 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2223 		s = stab(nbuf, ST_MAP, ST_FIND);
2224 		if (s == NULL)
2225 		{
2226 			syserr("Switch map %s: unknown member map %s",
2227 				map->map_mname, nbuf);
2228 		}
2229 		else
2230 		{
2231 			map->map_stack[mapno] = &s->s_map;
2232 			if (tTd(38, 4))
2233 				printf("\tmap_stack[%d] = %s:%s\n",
2234 					mapno, s->s_map.map_class->map_cname,
2235 					nbuf);
2236 		}
2237 	}
2238 	return TRUE;
2239 }
2240 
2241 
2242 /*
2243 **  SEQ_MAP_CLOSE -- close all underlying maps
2244 */
2245 
2246 seq_map_close(map)
2247 	MAP *map;
2248 {
2249 	int mapno;
2250 
2251 	if (tTd(38, 20))
2252 		printf("seq_map_close(%s)\n", map->map_mname);
2253 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2254 	{
2255 		MAP *mm = map->map_stack[mapno];
2256 
2257 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2258 			continue;
2259 		mm->map_class->map_close(mm);
2260 	}
2261 }
2262 
2263 
2264 /*
2265 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2266 */
2267 
2268 char *
2269 seq_map_lookup(map, key, args, pstat)
2270 	MAP *map;
2271 	char *key;
2272 	char **args;
2273 	int *pstat;
2274 {
2275 	int mapno;
2276 	int mapbit = 0x01;
2277 
2278 	if (tTd(38, 20))
2279 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2280 
2281 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2282 	{
2283 		MAP *mm = map->map_stack[mapno];
2284 		int stat = 0;
2285 		char *rv;
2286 
2287 		if (mm == NULL)
2288 			continue;
2289 		if (!bitset(MF_OPEN, mm->map_mflags))
2290 		{
2291 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2292 			{
2293 				*pstat = EX_UNAVAILABLE;
2294 				return NULL;
2295 			}
2296 			continue;
2297 		}
2298 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2299 		if (rv != NULL)
2300 			return rv;
2301 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2302 			return NULL;
2303 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2304 		{
2305 			*pstat = stat;
2306 			return NULL;
2307 		}
2308 	}
2309 	return NULL;
2310 }
2311 
2312 
2313 /*
2314 **  SEQ_MAP_STORE -- sequenced map store
2315 */
2316 
2317 void
2318 seq_map_store(map, key, val)
2319 	MAP *map;
2320 	char *key;
2321 	char *val;
2322 {
2323 	int mapno;
2324 
2325 	if (tTd(38, 12))
2326 		printf("seq_map_store(%s, %s, %s)\n",
2327 			map->map_mname, key, val);
2328 
2329 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2330 	{
2331 		MAP *mm = map->map_stack[mapno];
2332 
2333 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2334 			continue;
2335 
2336 		mm->map_class->map_store(mm, key, val);
2337 		return;
2338 	}
2339 	syserr("seq_map_store(%s, %s, %s): no writable map",
2340 		map->map_mname, key, val);
2341 }
2342 /*
2343 **  NULL stubs
2344 */
2345 
2346 bool
2347 null_map_open(map, mode)
2348 	MAP *map;
2349 	int mode;
2350 {
2351 	return TRUE;
2352 }
2353 
2354 void
2355 null_map_close(map)
2356 	MAP *map;
2357 {
2358 	return;
2359 }
2360 
2361 void
2362 null_map_store(map, key, val)
2363 	MAP *map;
2364 	char *key;
2365 	char *val;
2366 {
2367 	return;
2368 }
2369 
2370 
2371 /*
2372 **  BOGUS stubs
2373 */
2374 
2375 char *
2376 bogus_map_lookup(map, key, args, pstat)
2377 	MAP *map;
2378 	char *key;
2379 	char **args;
2380 	int *pstat;
2381 {
2382 	*pstat = EX_TEMPFAIL;
2383 	return NULL;
2384 }
2385 
2386 MAPCLASS	BogusMapClass =
2387 {
2388 	"bogus-map",		NULL,		0,
2389 	NULL,		bogus_map_lookup,	null_map_store,
2390 	null_map_open,	null_map_close,
2391 };
2392