xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision dd75f133)
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.56 (Berkeley) 04/21/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 defined(O_EXLOCK) && HASFLOCK
732 	if (fd >= 0)
733 	{
734 		if (mode == O_RDONLY)
735 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
736 		else
737 			map->map_mflags |= MF_LOCKED;
738 	}
739 # else
740 	if (mode == O_RDWR && fd >= 0)
741 	{
742 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
743 			map->map_mflags |= MF_LOCKED;
744 	}
745 # endif
746 #endif
747 
748 	/* try to make sure that at least the database header is on disk */
749 	if (mode == O_RDWR)
750 #if OLD_NEWDB
751 		(void) db->sync(db);
752 #else
753 		(void) db->sync(db, 0);
754 
755 	if (fd >= 0 && fstat(fd, &st) >= 0)
756 		map->map_mtime = st.st_mtime;
757 #endif
758 
759 	map->map_db2 = (void *) db;
760 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
761 		if (!aliaswait(map, ".db", TRUE))
762 			return FALSE;
763 	return TRUE;
764 }
765 
766 
767 /*
768 **  HASH_MAP_INIT -- HASH-style map initialization
769 */
770 
771 bool
772 hash_map_open(map, mode)
773 	MAP *map;
774 	int mode;
775 {
776 	DB *db;
777 	int i;
778 	int omode;
779 	int fd;
780 	struct stat st;
781 	char buf[MAXNAME + 1];
782 
783 	if (tTd(38, 2))
784 		printf("hash_map_open(%s, %s, %d)\n",
785 			map->map_mname, map->map_file, mode);
786 
787 	omode = mode;
788 	if (omode == O_RDWR)
789 	{
790 		omode |= O_CREAT|O_TRUNC;
791 #if defined(O_EXLOCK) && HASFLOCK
792 		omode |= O_EXLOCK;
793 # if !OLD_NEWDB
794 	}
795 	else
796 	{
797 		omode |= O_SHLOCK;
798 # endif
799 #endif
800 	}
801 
802 	(void) strcpy(buf, map->map_file);
803 	i = strlen(buf);
804 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
805 		(void) strcat(buf, ".db");
806 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
807 	if (db == NULL)
808 	{
809 #ifdef MAYBENEXTRELEASE
810 		if (aliaswait(map, ".db", FALSE))
811 			return TRUE;
812 #endif
813 		if (!bitset(MF_OPTIONAL, map->map_mflags))
814 			syserr("Cannot open HASH database %s", map->map_file);
815 		return FALSE;
816 	}
817 #if !OLD_NEWDB
818 	fd = db->fd(db);
819 # if defined(O_EXLOCK) && HASFLOCK
820 	if (fd >= 0)
821 	{
822 		if (mode == O_RDONLY)
823 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
824 		else
825 			map->map_mflags |= MF_LOCKED;
826 	}
827 # else
828 	if (mode == O_RDWR && fd >= 0)
829 	{
830 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
831 			map->map_mflags |= MF_LOCKED;
832 	}
833 # endif
834 #endif
835 
836 	/* try to make sure that at least the database header is on disk */
837 	if (mode == O_RDWR)
838 #if OLD_NEWDB
839 		(void) db->sync(db);
840 #else
841 		(void) db->sync(db, 0);
842 
843 	if (fd >= 0 && fstat(fd, &st) >= 0)
844 		map->map_mtime = st.st_mtime;
845 #endif
846 
847 	map->map_db2 = (void *) db;
848 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
849 		if (!aliaswait(map, ".db", TRUE))
850 			return FALSE;
851 	return TRUE;
852 }
853 
854 
855 /*
856 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
857 */
858 
859 char *
860 db_map_lookup(map, name, av, statp)
861 	MAP *map;
862 	char *name;
863 	char **av;
864 	int *statp;
865 {
866 	DBT key, val;
867 	register DB *db = (DB *) map->map_db2;
868 	int st;
869 	int saveerrno;
870 	int fd;
871 	char keybuf[MAXNAME + 1];
872 
873 	if (tTd(38, 20))
874 		printf("db_map_lookup(%s, %s)\n",
875 			map->map_mname, name);
876 
877 	key.size = strlen(name);
878 	if (key.size > sizeof keybuf - 1)
879 		key.size = sizeof keybuf - 1;
880 	key.data = keybuf;
881 	bcopy(name, keybuf, key.size + 1);
882 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
883 		makelower(keybuf);
884 #if !OLD_NEWDB
885 	fd = db->fd(db);
886 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
887 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
888 #endif
889 	st = 1;
890 	if (bitset(MF_TRY0NULL, map->map_mflags))
891 	{
892 		st = db->get(db, &key, &val, 0);
893 		if (st == 0)
894 			map->map_mflags &= ~MF_TRY1NULL;
895 	}
896 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
897 	{
898 		key.size++;
899 		st = db->get(db, &key, &val, 0);
900 		if (st == 0)
901 			map->map_mflags &= ~MF_TRY0NULL;
902 	}
903 	saveerrno = errno;
904 #if !OLD_NEWDB
905 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
906 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
907 #endif
908 	if (st != 0)
909 	{
910 		errno = saveerrno;
911 		if (st < 0)
912 			syserr("db_map_lookup: get (%s)", name);
913 		return NULL;
914 	}
915 	if (bitset(MF_MATCHONLY, map->map_mflags))
916 		return map_rewrite(map, name, strlen(name), NULL);
917 	else
918 		return map_rewrite(map, val.data, val.size, av);
919 }
920 
921 
922 /*
923 **  DB_MAP_STORE -- store a datum in the NEWDB database
924 */
925 
926 void
927 db_map_store(map, lhs, rhs)
928 	register MAP *map;
929 	char *lhs;
930 	char *rhs;
931 {
932 	int stat;
933 	DBT key;
934 	DBT data;
935 	register DB *db = map->map_db2;
936 
937 	if (tTd(38, 20))
938 		printf("db_map_store(%s, %s, %s)\n",
939 			map->map_mname, lhs, rhs);
940 
941 	key.size = strlen(lhs);
942 	key.data = lhs;
943 
944 	data.size = strlen(rhs);
945 	data.data = rhs;
946 
947 	if (bitset(MF_INCLNULL, map->map_mflags))
948 	{
949 		key.size++;
950 		data.size++;
951 	}
952 
953 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
954 	if (stat > 0)
955 	{
956 		if (!bitset(MF_APPEND, map->map_mflags))
957 			usrerr("050 Warning: duplicate alias name %s", lhs);
958 		else
959 		{
960 			static char *buf = NULL;
961 			static int bufsiz = 0;
962 			DBT old;
963 
964 			old.data = db_map_lookup(map, key.data, NULL, &stat);
965 			if (old.data != NULL)
966 			{
967 				old.size = strlen(old.data);
968 				if (data.size + old.size + 2 > bufsiz)
969 				{
970 					if (buf != NULL)
971 						(void) free(buf);
972 					bufsiz = data.size + old.size + 2;
973 					buf = xalloc(bufsiz);
974 				}
975 				sprintf(buf, "%s,%s", data.data, old.data);
976 				data.size = data.size + old.size + 1;
977 				data.data = buf;
978 				if (tTd(38, 9))
979 					printf("db_map_store append=%s\n", data.data);
980 			}
981 		}
982 		stat = db->put(db, &key, &data, 0);
983 	}
984 	if (stat != 0)
985 		syserr("readaliases: db put (%s)", lhs);
986 }
987 
988 
989 /*
990 **  DB_MAP_CLOSE -- add distinguished entries and close the database
991 */
992 
993 void
994 db_map_close(map)
995 	MAP *map;
996 {
997 	register DB *db = map->map_db2;
998 
999 	if (tTd(38, 9))
1000 		printf("db_map_close(%s, %s, %x)\n",
1001 			map->map_mname, map->map_file, map->map_mflags);
1002 
1003 	if (bitset(MF_WRITABLE, map->map_mflags))
1004 	{
1005 		/* write out the distinguished alias */
1006 		db_map_store(map, "@", "@");
1007 	}
1008 
1009 	if (db->close(db) != 0)
1010 		syserr("readaliases: db close failure");
1011 }
1012 
1013 #endif
1014 /*
1015 **  NIS Modules
1016 */
1017 
1018 # ifdef NIS
1019 
1020 # ifndef YPERR_BUSY
1021 #  define YPERR_BUSY	16
1022 # endif
1023 
1024 /*
1025 **  NIS_MAP_OPEN -- open DBM map
1026 */
1027 
1028 bool
1029 nis_map_open(map, mode)
1030 	MAP *map;
1031 	int mode;
1032 {
1033 	int yperr;
1034 	register char *p;
1035 	auto char *vp;
1036 	auto int vsize;
1037 	char *master;
1038 
1039 	if (tTd(38, 2))
1040 		printf("nis_map_open(%s, %s)\n",
1041 			map->map_mname, map->map_file);
1042 
1043 	if (mode != O_RDONLY)
1044 	{
1045 		/* issue a pseudo-error message */
1046 #ifdef ENOSYS
1047 		errno = ENOSYS;
1048 #else
1049 # ifdef EFTYPE
1050 		errno = EFTYPE;
1051 # else
1052 		errno = ENXIO;
1053 # endif
1054 #endif
1055 		return FALSE;
1056 	}
1057 
1058 	p = strchr(map->map_file, '@');
1059 	if (p != NULL)
1060 	{
1061 		*p++ = '\0';
1062 		if (*p != '\0')
1063 			map->map_domain = p;
1064 	}
1065 
1066 	if (*map->map_file == '\0')
1067 		map->map_file = "mail.aliases";
1068 
1069 	if (map->map_domain == NULL)
1070 	{
1071 		yperr = yp_get_default_domain(&map->map_domain);
1072 		if (yperr != 0)
1073 		{
1074 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1075 				syserr("421 NIS map %s specified, but NIS not running\n",
1076 					map->map_file);
1077 			return FALSE;
1078 		}
1079 	}
1080 
1081 	/* check to see if this map actually exists */
1082 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1083 			&vp, &vsize);
1084 	if (tTd(38, 10))
1085 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1086 			map->map_domain, map->map_file, yperr_string(yperr));
1087 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1088 	{
1089 		if (!bitset(MF_ALIAS, map->map_mflags) ||
1090 		    aliaswait(map, NULL, TRUE))
1091 			return TRUE;
1092 	}
1093 
1094 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1095 	{
1096 		syserr("421 Cannot bind to map %s in domain %s: %s",
1097 			map->map_file, map->map_domain, yperr_string(yperr));
1098 	}
1099 
1100 	return FALSE;
1101 }
1102 
1103 
1104 /*
1105 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1106 */
1107 
1108 char *
1109 nis_map_lookup(map, name, av, statp)
1110 	MAP *map;
1111 	char *name;
1112 	char **av;
1113 	int *statp;
1114 {
1115 	char *vp;
1116 	auto int vsize;
1117 	int buflen;
1118 	int yperr;
1119 	char keybuf[MAXNAME + 1];
1120 
1121 	if (tTd(38, 20))
1122 		printf("nis_map_lookup(%s, %s)\n",
1123 			map->map_mname, name);
1124 
1125 	buflen = strlen(name);
1126 	if (buflen > sizeof keybuf - 1)
1127 		buflen = sizeof keybuf - 1;
1128 	bcopy(name, keybuf, buflen + 1);
1129 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1130 		makelower(keybuf);
1131 	yperr = YPERR_KEY;
1132 	if (bitset(MF_TRY0NULL, map->map_mflags))
1133 	{
1134 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1135 			     &vp, &vsize);
1136 		if (yperr == 0)
1137 			map->map_mflags &= ~MF_TRY1NULL;
1138 	}
1139 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1140 	{
1141 		buflen++;
1142 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1143 			     &vp, &vsize);
1144 		if (yperr == 0)
1145 			map->map_mflags &= ~MF_TRY0NULL;
1146 	}
1147 	if (yperr != 0)
1148 	{
1149 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1150 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1151 		return NULL;
1152 	}
1153 	if (bitset(MF_MATCHONLY, map->map_mflags))
1154 		return map_rewrite(map, name, strlen(name), NULL);
1155 	else
1156 		return map_rewrite(map, vp, vsize, av);
1157 }
1158 
1159 #endif
1160 /*
1161 **  NISPLUS Modules
1162 **
1163 **	This code donated by Sun Microsystems.
1164 */
1165 
1166 #ifdef NISPLUS
1167 
1168 #undef NIS /* symbol conflict in nis.h */
1169 #include <rpcsvc/nis.h>
1170 #include <rpcsvc/nislib.h>
1171 
1172 #define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1173 #define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1174 #define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1175 #define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
1176 
1177 /*
1178 **  NISPLUS_MAP_OPEN -- open nisplus table
1179 */
1180 
1181 bool
1182 nisplus_map_open(map, mode)
1183 	MAP *map;
1184 	int mode;
1185 {
1186 	register char *p;
1187 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1188 	nis_result *res = NULL;
1189 	u_int objs_len;
1190 	nis_object *obj_ptr;
1191 	int retry_cnt, max_col, i;
1192 
1193 	if (tTd(38, 2))
1194 		printf("nisplus_map_open(%s, %s, %d)\n",
1195 			map->map_mname, map->map_file, mode);
1196 
1197 	if (mode != O_RDONLY)
1198 	{
1199 		errno = ENODEV;
1200 		return FALSE;
1201 	}
1202 
1203 	if (*map->map_file == '\0')
1204 		map->map_file = "mail_aliases.org_dir";
1205 
1206 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1207 	{
1208 		/* set default NISPLUS Domain to $m */
1209 		extern char *nisplus_default_domain();
1210 
1211 		map->map_domain = newstr(nisplus_default_domain());
1212 		if (tTd(38, 2))
1213 			printf("nisplus_map_open(%s): using domain %s\n",
1214 				 map->map_file, map->map_domain);
1215 	}
1216 	if (!PARTIAL_NAME(map->map_file))
1217 		map->map_domain = newstr("");
1218 
1219 	/* check to see if this map actually exists */
1220 	if (PARTIAL_NAME(map->map_file))
1221 		sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
1222 	else
1223 		strcpy(qbuf, map->map_file);
1224 
1225 	retry_cnt = 0;
1226 	while (res == NULL || res->status != NIS_SUCCESS)
1227 	{
1228 		res = nis_lookup(qbuf, FOLLOW_LINKS);
1229 		switch (res->status)
1230 		{
1231 		  case NIS_SUCCESS:
1232 		  case NIS_TRYAGAIN:
1233 		  case NIS_RPCERROR:
1234 		  case NIS_NAMEUNREACHABLE:
1235 			break;
1236 
1237 		  default:		/* all other nisplus errors */
1238 #if 0
1239 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1240 				syserr("421 Cannot find table %s.%s: %s",
1241 					map->map_file, map->map_domain,
1242 					nis_sperrno(res->status));
1243 #endif
1244 			errno = EBADR;
1245 			return FALSE;
1246 		}
1247 		sleep(2);		/* try not to overwhelm hosed server */
1248 		if (retry_cnt++ > 4)
1249 		{
1250 			errno = EBADR;
1251 			return FALSE;
1252 		}
1253 	}
1254 
1255 	if (NIS_RES_NUMOBJ(res) != 1 ||
1256 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1257 	{
1258 		if (tTd(38, 10))
1259 			printf("nisplus_map_open: %s is not a table\n", qbuf);
1260 #if 0
1261 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1262 			syserr("421 %s.%s: %s is not a table",
1263 				map->map_file, map->map_domain,
1264 				nis_sperrno(res->status));
1265 #endif
1266 		errno = EBADR;
1267 		return FALSE;
1268 	}
1269 	/* default key column is column 0 */
1270 	if (map->map_keycolnm == NULL)
1271 		map->map_keycolnm = newstr(COL_NAME(res,0));
1272 
1273 	max_col = COL_MAX(res);
1274 
1275 	/* verify the key column exist */
1276 	for (i=0; i< max_col; i++)
1277 	{
1278 		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
1279 			break;
1280 	}
1281 	if (i == max_col)
1282 	{
1283 		if (tTd(38, 2))
1284 			printf("nisplus_map_open(%s): can not find key column %s\n",
1285 				map->map_file, map->map_keycolnm);
1286 		errno = EBADR;
1287 		return FALSE;
1288 	}
1289 
1290 	/* default value column is the last column */
1291 	if (map->map_valcolnm == NULL)
1292 	{
1293 		map->map_valcolno = max_col - 1;
1294 		return TRUE;
1295 	}
1296 
1297 	for (i=0; i< max_col; i++)
1298 	{
1299 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1300 		{
1301 			map->map_valcolno = i;
1302 			return TRUE;
1303 		}
1304 	}
1305 
1306 	if (tTd(38, 2))
1307 		printf("nisplus_map_open(%s): can not find column %s\n",
1308 			 map->map_file, map->map_keycolnm);
1309 	errno = EBADR;
1310 	return FALSE;
1311 }
1312 
1313 
1314 /*
1315 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1316 */
1317 
1318 char *
1319 nisplus_map_lookup(map, name, av, statp)
1320 	MAP *map;
1321 	char *name;
1322 	char **av;
1323 	int *statp;
1324 {
1325 	char *vp;
1326 	auto int vsize;
1327 	int buflen;
1328 	char search_key[MAXNAME + 1];
1329 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1330 	nis_result *result;
1331 
1332 	if (tTd(38, 20))
1333 		printf("nisplus_map_lookup(%s, %s)\n",
1334 			map->map_mname, name);
1335 
1336 	if (!bitset(MF_OPEN, map->map_mflags))
1337 	{
1338 		if (nisplus_map_open(map, O_RDONLY))
1339 			map->map_mflags |= MF_OPEN;
1340 		else
1341 		{
1342 			*statp = EX_UNAVAILABLE;
1343 			return NULL;
1344 		}
1345 	}
1346 
1347 	buflen = strlen(name);
1348 	if (buflen > sizeof search_key - 1)
1349 		buflen = sizeof search_key - 1;
1350 	bcopy(name, search_key, buflen + 1);
1351 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1352 		makelower(search_key);
1353 
1354 	/* construct the query */
1355 	if (PARTIAL_NAME(map->map_file))
1356 		sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
1357 			search_key, map->map_file, map->map_domain);
1358 	else
1359 		sprintf(qbuf, "[%s=%s],%s", map->map_keycolnm,
1360 			search_key, map->map_file);
1361 
1362 	if (tTd(38, 20))
1363 		printf("qbuf=%s\n", qbuf);
1364 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1365 	if (result->status == NIS_SUCCESS)
1366 	{
1367 		int count;
1368 		char *str;
1369 
1370 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1371 		{
1372 			if (LogLevel > 10)
1373 				syslog(LOG_WARNING,
1374 				  "%s:Lookup error, expected 1 entry, got (%d)",
1375 				    map->map_file, count);
1376 
1377 			/* ignore second entry */
1378 			if (tTd(38, 20))
1379 				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1380 					name, count);
1381 		}
1382 
1383 		vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1384 		/* set the length of the result */
1385 		if (vp == NULL)
1386 			vp = "";
1387 		vsize = strlen(vp);
1388 		if (tTd(38, 20))
1389 			printf("nisplus_map_lookup(%s), found %s\n",
1390 				name, vp);
1391 		if (bitset(MF_MATCHONLY, map->map_mflags))
1392 			str = map_rewrite(map, name, strlen(name), NULL);
1393 		else
1394 			str = map_rewrite(map, vp, vsize, av);
1395 		nis_freeresult(result);
1396 #ifdef MAP_EXIT_STAT
1397 		*statp = EX_OK;
1398 #endif
1399 		return str;
1400 	}
1401 	else
1402 	{
1403 #ifdef MAP_EXIT_STAT
1404 		if (result->status == NIS_NOTFOUND)
1405 			*statp = EX_NOTFOUND;
1406 		else if (result->status == NIS_TRYAGAIN)
1407 			*statp = EX_TEMPFAIL;
1408 		else
1409 		{
1410 			*statp = EX_UNAVAILABLE;
1411 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1412 		}
1413 #else
1414 		if ((result->status != NIS_NOTFOUND) &&
1415 		    (result->status != NIS_TRYAGAIN))
1416 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1417 #endif
1418 	}
1419 	if (tTd(38, 20))
1420 		printf("nisplus_map_lookup(%s), failed\n", name);
1421 	nis_freeresult(result);
1422 	return NULL;
1423 }
1424 
1425 
1426 char *
1427 nisplus_default_domain()
1428 {
1429 	static char default_domain[MAXNAME + 1] = "";
1430 	char *p;
1431 
1432 	if (default_domain[0] != '\0')
1433 		return(default_domain);
1434 
1435 	p = nis_local_directory();
1436 	strcpy(default_domain, p);
1437 	return default_domain;
1438 }
1439 
1440 #endif /* NISPLUS */
1441 /*
1442 **  HESIOD Modules
1443 */
1444 
1445 #ifdef HESIOD
1446 
1447 #include <hesiod.h>
1448 
1449 char *
1450 hes_map_lookup(map, name, av, statp)
1451         MAP *map;
1452         char *name;
1453         char **av;
1454         int *statp;
1455 {
1456 	char **hp;
1457 	char *retdata = NULL;
1458 	int i;
1459 
1460 	if (tTd(38, 20))
1461 		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1462 
1463 	hp = hes_resolve(name, map->map_file);
1464 	if (hp == NULL)
1465 		return NULL;
1466 
1467 	if (hp[0] != NULL)
1468 	{
1469 		if (bitset(MF_MATCHONLY, map->map_mflags))
1470 			retdata = map_rewrite(map, name, strlen(name), NULL);
1471 		else
1472 			retdata = map_rewrite(map, hp[0], strlen(hp[0]), av);
1473 	}
1474 
1475 	for (i = 0; hp[i] != NULL; i++)
1476 		free(hp[i]);
1477 	free(hp);
1478 	return retdata;
1479 }
1480 
1481 #endif
1482 /*
1483 **  NeXT NETINFO Modules
1484 */
1485 
1486 #ifdef NETINFO
1487 
1488 #define NETINFO_DEFAULT_DIR		"/aliases"
1489 #define NETINFO_DEFAULT_PROPERTY	"members"
1490 
1491 
1492 /*
1493 **  NI_MAP_OPEN -- open NetInfo Aliases
1494 */
1495 
1496 bool
1497 ni_map_open(map, mode)
1498 	MAP *map;
1499 	int mode;
1500 {
1501 	char *p;
1502 
1503 	if (tTd(38, 20))
1504 		printf("ni_map_open: %s\n", map->map_file);
1505 
1506 	if (*map->map_file == '\0')
1507 		map->map_file = NETINFO_DEFAULT_DIR;
1508 
1509 	if (map->map_valcolnm == NULL)
1510 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1511 
1512 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1513 		map->map_coldelim = ',';
1514 
1515 	return TRUE;
1516 }
1517 
1518 
1519 /*
1520 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1521 */
1522 
1523 char *
1524 ni_map_lookup(map, name, av, statp)
1525 	MAP *map;
1526 	char *name;
1527 	char **av;
1528 	int *statp;
1529 {
1530 	char *res;
1531 	char *propval;
1532 	extern char *ni_propval();
1533 
1534 	if (tTd(38, 20))
1535 		printf("ni_map_lookup(%s, %s)\n",
1536 			map->map_mname, name);
1537 
1538 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1539 			     map->map_valcolnm, map->map_coldelim);
1540 
1541 	if (propval == NULL)
1542 		return NULL;
1543 
1544 	if (bitset(MF_MATCHONLY, map->map_mflags))
1545 		res = map_rewrite(map, name, strlen(name), NULL);
1546 	else
1547 		res = map_rewrite(map, propval, strlen(propval), av);
1548 	free(propval);
1549 	return res;
1550 }
1551 
1552 #endif
1553 /*
1554 **  TEXT (unindexed text file) Modules
1555 **
1556 **	This code donated by Sun Microsystems.
1557 */
1558 
1559 
1560 /*
1561 **  TEXT_MAP_OPEN -- open text table
1562 */
1563 
1564 bool
1565 text_map_open(map, mode)
1566 	MAP *map;
1567 	int mode;
1568 {
1569 	struct stat sbuf;
1570 
1571 	if (tTd(38, 2))
1572 		printf("text_map_open(%s, %s, %d)\n",
1573 			map->map_mname, map->map_file, mode);
1574 
1575 	if (mode != O_RDONLY)
1576 	{
1577 		errno = ENODEV;
1578 		return FALSE;
1579 	}
1580 
1581 	if (*map->map_file == '\0')
1582 	{
1583 		if (tTd(38, 2))
1584 			printf("text_map_open: file name required\n");
1585 		return FALSE;
1586 	}
1587 
1588 	if (map->map_file[0] != '/')
1589 	{
1590 		if (tTd(38, 2))
1591 			printf("text_map_open(%s): file name must be fully qualified\n",
1592 				map->map_file);
1593 		return FALSE;
1594 	}
1595 	/* check to see if this map actually accessable */
1596 	if (access(map->map_file, R_OK) <0)
1597 		return FALSE;
1598 
1599 	/* check to see if this map actually exist */
1600 	if (stat(map->map_file, &sbuf) <0)
1601 	{
1602 		if (tTd(38, 2))
1603 			printf("text_map_open(%s): can not stat %s\n",
1604 				map->map_file, map->map_file);
1605 		return FALSE;
1606 	}
1607 
1608 	if (!S_ISREG(sbuf.st_mode))
1609 	{
1610 		if (tTd(38, 2))
1611 			printf("text_map_open(%s): %s is not a file\n",
1612 				map->map_file, map->map_file);
1613 		return FALSE;
1614 	}
1615 
1616 	if (map->map_keycolnm == NULL)
1617 		map->map_keycolno = 0;
1618 	else
1619 	{
1620 		if (!isdigit(*map->map_keycolnm))
1621 		{
1622 			if (tTd(38, 2))
1623 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1624 					map->map_file, map->map_keycolnm);
1625 			return FALSE;
1626 		}
1627 		map->map_keycolno = atoi(map->map_keycolnm);
1628 	}
1629 
1630 	if (map->map_valcolnm == NULL)
1631 		map->map_valcolno = 0;
1632 	else
1633 	{
1634 		if (!isdigit(*map->map_valcolnm))
1635 		{
1636 			if (tTd(38, 2))
1637 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1638 					map->map_file, map->map_valcolnm);
1639 			return FALSE;
1640 		}
1641 		map->map_valcolno = atoi(map->map_valcolnm);
1642 	}
1643 
1644 	if (tTd(38, 2))
1645 	{
1646 		printf("text_map_open(%s): delimiter = ",
1647 			map->map_file);
1648 		if (map->map_coldelim == '\0')
1649 			printf("(white space)\n");
1650 		else
1651 			printf("%c\n", map->map_coldelim);
1652 	}
1653 
1654 	return TRUE;
1655 }
1656 
1657 
1658 /*
1659 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1660 */
1661 
1662 char *
1663 text_map_lookup(map, name, av, statp)
1664 	MAP *map;
1665 	char *name;
1666 	char **av;
1667 	int *statp;
1668 {
1669 	char *vp;
1670 	auto int vsize;
1671 	int buflen;
1672 	char search_key[MAXNAME + 1];
1673 	char linebuf[MAXLINE];
1674 	FILE *f;
1675 	char buf[MAXNAME + 1];
1676 	char delim;
1677 	int key_idx;
1678 	bool found_it;
1679 	extern char *get_column();
1680 
1681 
1682 	found_it = FALSE;
1683 	if (tTd(38, 20))
1684 		printf("text_map_lookup(%s)\n", name);
1685 
1686 	buflen = strlen(name);
1687 	if (buflen > sizeof search_key - 1)
1688 		buflen = sizeof search_key - 1;
1689 	bcopy(name, search_key, buflen + 1);
1690 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1691 		makelower(search_key);
1692 
1693 	f = fopen(map->map_file, "r");
1694 	if (f == NULL)
1695 	{
1696 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1697 		*statp = EX_UNAVAILABLE;
1698 		return NULL;
1699 	}
1700 	key_idx = map->map_keycolno;
1701 	delim = map->map_coldelim;
1702 	while (fgets(linebuf, MAXLINE, f))
1703 	{
1704 		char *lf;
1705 		if (linebuf[0] == '#')
1706 			continue; /* skip comment line */
1707 		if (lf = strchr(linebuf, '\n'))
1708 			*lf = '\0';
1709 		if (!strcasecmp(search_key,
1710 				get_column(linebuf, key_idx, delim, buf)))
1711 		{
1712 			found_it = TRUE;
1713 			break;
1714 		}
1715 	}
1716 	fclose(f);
1717 	if (!found_it)
1718 	{
1719 #ifdef MAP_EXIT_STAT
1720 		*statp = EX_NOTFOUND;
1721 #endif
1722 		return(NULL);
1723 	}
1724 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1725 	vsize = strlen(vp);
1726 #ifdef MAP_EXIT_STAT
1727 	*statp = EX_OK;
1728 #endif
1729 	if (bitset(MF_MATCHONLY, map->map_mflags))
1730 		return map_rewrite(map, name, strlen(name), NULL);
1731 	else
1732 		return map_rewrite(map, vp, vsize, av);
1733 }
1734 /*
1735 **  STAB (Symbol Table) Modules
1736 */
1737 
1738 
1739 /*
1740 **  STAB_MAP_LOOKUP -- look up alias in symbol table
1741 */
1742 
1743 char *
1744 stab_map_lookup(map, name, av, pstat)
1745 	register MAP *map;
1746 	char *name;
1747 	char **av;
1748 	int *pstat;
1749 {
1750 	register STAB *s;
1751 
1752 	if (tTd(38, 20))
1753 		printf("stab_lookup(%s, %s)\n",
1754 			map->map_mname, name);
1755 
1756 	s = stab(name, ST_ALIAS, ST_FIND);
1757 	if (s != NULL)
1758 		return (s->s_alias);
1759 	return (NULL);
1760 }
1761 
1762 
1763 /*
1764 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1765 */
1766 
1767 void
1768 stab_map_store(map, lhs, rhs)
1769 	register MAP *map;
1770 	char *lhs;
1771 	char *rhs;
1772 {
1773 	register STAB *s;
1774 
1775 	s = stab(lhs, ST_ALIAS, ST_ENTER);
1776 	s->s_alias = newstr(rhs);
1777 }
1778 
1779 
1780 /*
1781 **  STAB_MAP_OPEN -- initialize (reads data file)
1782 **
1783 **	This is a wierd case -- it is only intended as a fallback for
1784 **	aliases.  For this reason, opens for write (only during a
1785 **	"newaliases") always fails, and opens for read open the
1786 **	actual underlying text file instead of the database.
1787 */
1788 
1789 bool
1790 stab_map_open(map, mode)
1791 	register MAP *map;
1792 	int mode;
1793 {
1794 	FILE *af;
1795 	struct stat st;
1796 
1797 	if (tTd(38, 2))
1798 		printf("stab_map_open(%s, %s)\n",
1799 			map->map_mname, map->map_file);
1800 
1801 	if (mode != O_RDONLY)
1802 	{
1803 		errno = ENODEV;
1804 		return FALSE;
1805 	}
1806 
1807 	af = fopen(map->map_file, "r");
1808 	if (af == NULL)
1809 		return FALSE;
1810 	readaliases(map, af, FALSE, FALSE);
1811 
1812 	if (fstat(fileno(af), &st) >= 0)
1813 		map->map_mtime = st.st_mtime;
1814 	fclose(af);
1815 
1816 	return TRUE;
1817 }
1818 /*
1819 **  Implicit Modules
1820 **
1821 **	Tries several types.  For back compatibility of aliases.
1822 */
1823 
1824 
1825 /*
1826 **  IMPL_MAP_LOOKUP -- lookup in best open database
1827 */
1828 
1829 char *
1830 impl_map_lookup(map, name, av, pstat)
1831 	MAP *map;
1832 	char *name;
1833 	char **av;
1834 	int *pstat;
1835 {
1836 	if (tTd(38, 20))
1837 		printf("impl_map_lookup(%s, %s)\n",
1838 			map->map_mname, name);
1839 
1840 #ifdef NEWDB
1841 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1842 		return db_map_lookup(map, name, av, pstat);
1843 #endif
1844 #ifdef NDBM
1845 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1846 		return ndbm_map_lookup(map, name, av, pstat);
1847 #endif
1848 	return stab_map_lookup(map, name, av, pstat);
1849 }
1850 
1851 /*
1852 **  IMPL_MAP_STORE -- store in open databases
1853 */
1854 
1855 void
1856 impl_map_store(map, lhs, rhs)
1857 	MAP *map;
1858 	char *lhs;
1859 	char *rhs;
1860 {
1861 #ifdef NEWDB
1862 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1863 		db_map_store(map, lhs, rhs);
1864 #endif
1865 #ifdef NDBM
1866 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1867 		ndbm_map_store(map, lhs, rhs);
1868 #endif
1869 	stab_map_store(map, lhs, rhs);
1870 }
1871 
1872 /*
1873 **  IMPL_MAP_OPEN -- implicit database open
1874 */
1875 
1876 bool
1877 impl_map_open(map, mode)
1878 	MAP *map;
1879 	int mode;
1880 {
1881 	struct stat stb;
1882 
1883 	if (tTd(38, 2))
1884 		printf("impl_map_open(%s, %s, %d)\n",
1885 			map->map_mname, map->map_file, mode);
1886 
1887 	if (stat(map->map_file, &stb) < 0)
1888 	{
1889 		/* no alias file at all */
1890 		if (tTd(38, 3))
1891 			printf("no map file\n");
1892 		return FALSE;
1893 	}
1894 
1895 #ifdef NEWDB
1896 	map->map_mflags |= MF_IMPL_HASH;
1897 	if (hash_map_open(map, mode))
1898 	{
1899 #if defined(NDBM) && defined(NIS)
1900 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
1901 #endif
1902 			return TRUE;
1903 	}
1904 	else
1905 		map->map_mflags &= ~MF_IMPL_HASH;
1906 #endif
1907 #ifdef NDBM
1908 	map->map_mflags |= MF_IMPL_NDBM;
1909 	if (ndbm_map_open(map, mode))
1910 	{
1911 		return TRUE;
1912 	}
1913 	else
1914 		map->map_mflags &= ~MF_IMPL_NDBM;
1915 #endif
1916 
1917 #if defined(NEWDB) || defined(NDBM)
1918 	if (Verbose)
1919 		message("WARNING: cannot open alias database %s", map->map_file);
1920 #else
1921 	if (mode != O_RDONLY)
1922 		usrerr("Cannot rebuild aliases: no database format defined");
1923 #endif
1924 
1925 	return stab_map_open(map, mode);
1926 }
1927 
1928 
1929 /*
1930 **  IMPL_MAP_CLOSE -- close any open database(s)
1931 */
1932 
1933 void
1934 impl_map_close(map)
1935 	MAP *map;
1936 {
1937 	if (tTd(38, 20))
1938 		printf("impl_map_close(%s, %s, %x)\n",
1939 			map->map_mname, map->map_file, map->map_mflags);
1940 #ifdef NEWDB
1941 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1942 	{
1943 		db_map_close(map);
1944 		map->map_mflags &= ~MF_IMPL_HASH;
1945 	}
1946 #endif
1947 
1948 #ifdef NDBM
1949 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1950 	{
1951 		ndbm_map_close(map);
1952 		map->map_mflags &= ~MF_IMPL_NDBM;
1953 	}
1954 #endif
1955 }
1956 /*
1957 **  User map class.
1958 **
1959 **	Provides access to the system password file.
1960 */
1961 
1962 /*
1963 **  USER_MAP_OPEN -- open user map
1964 **
1965 **	Really just binds field names to field numbers.
1966 */
1967 
1968 bool
1969 user_map_open(map, mode)
1970 	MAP *map;
1971 	int mode;
1972 {
1973 	if (tTd(38, 2))
1974 		printf("user_map_open(%s)\n", map->map_mname);
1975 
1976 	if (mode != O_RDONLY)
1977 	{
1978 		/* issue a pseudo-error message */
1979 #ifdef ENOSYS
1980 		errno = ENOSYS;
1981 #else
1982 # ifdef EFTYPE
1983 		errno = EFTYPE;
1984 # else
1985 		errno = ENXIO;
1986 # endif
1987 #endif
1988 		return FALSE;
1989 	}
1990 	if (map->map_valcolnm == NULL)
1991 		/* nothing */ ;
1992 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
1993 		map->map_valcolno = 1;
1994 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
1995 		map->map_valcolno = 2;
1996 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
1997 		map->map_valcolno = 3;
1998 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
1999 		map->map_valcolno = 4;
2000 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
2001 		map->map_valcolno = 5;
2002 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2003 		map->map_valcolno = 6;
2004 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2005 		map->map_valcolno = 7;
2006 	else
2007 	{
2008 		syserr("User map %s: unknown column name %s",
2009 			map->map_mname, map->map_valcolnm);
2010 		return FALSE;
2011 	}
2012 	return TRUE;
2013 }
2014 
2015 
2016 /*
2017 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2018 */
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 = sm_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 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
2261 	}
2262 }
2263 
2264 
2265 /*
2266 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2267 */
2268 
2269 char *
2270 seq_map_lookup(map, key, args, pstat)
2271 	MAP *map;
2272 	char *key;
2273 	char **args;
2274 	int *pstat;
2275 {
2276 	int mapno;
2277 	int mapbit = 0x01;
2278 
2279 	if (tTd(38, 20))
2280 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2281 
2282 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2283 	{
2284 		MAP *mm = map->map_stack[mapno];
2285 		int stat = 0;
2286 		char *rv;
2287 
2288 		if (mm == NULL)
2289 			continue;
2290 		if (!bitset(MF_OPEN, mm->map_mflags))
2291 		{
2292 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2293 			{
2294 				*pstat = EX_UNAVAILABLE;
2295 				return NULL;
2296 			}
2297 			continue;
2298 		}
2299 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2300 		if (rv != NULL)
2301 			return rv;
2302 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2303 			return NULL;
2304 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2305 		{
2306 			*pstat = stat;
2307 			return NULL;
2308 		}
2309 	}
2310 	return NULL;
2311 }
2312 
2313 
2314 /*
2315 **  SEQ_MAP_STORE -- sequenced map store
2316 */
2317 
2318 void
2319 seq_map_store(map, key, val)
2320 	MAP *map;
2321 	char *key;
2322 	char *val;
2323 {
2324 	int mapno;
2325 
2326 	if (tTd(38, 12))
2327 		printf("seq_map_store(%s, %s, %s)\n",
2328 			map->map_mname, key, val);
2329 
2330 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2331 	{
2332 		MAP *mm = map->map_stack[mapno];
2333 
2334 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2335 			continue;
2336 
2337 		mm->map_class->map_store(mm, key, val);
2338 		return;
2339 	}
2340 	syserr("seq_map_store(%s, %s, %s): no writable map",
2341 		map->map_mname, key, val);
2342 }
2343 /*
2344 **  NULL stubs
2345 */
2346 
2347 bool
2348 null_map_open(map, mode)
2349 	MAP *map;
2350 	int mode;
2351 {
2352 	return TRUE;
2353 }
2354 
2355 void
2356 null_map_close(map)
2357 	MAP *map;
2358 {
2359 	return;
2360 }
2361 
2362 void
2363 null_map_store(map, key, val)
2364 	MAP *map;
2365 	char *key;
2366 	char *val;
2367 {
2368 	return;
2369 }
2370 
2371 
2372 /*
2373 **  BOGUS stubs
2374 */
2375 
2376 char *
2377 bogus_map_lookup(map, key, args, pstat)
2378 	MAP *map;
2379 	char *key;
2380 	char **args;
2381 	int *pstat;
2382 {
2383 	*pstat = EX_TEMPFAIL;
2384 	return NULL;
2385 }
2386 
2387 MAPCLASS	BogusMapClass =
2388 {
2389 	"bogus-map",		NULL,		0,
2390 	NULL,		bogus_map_lookup,	null_map_store,
2391 	null_map_open,	null_map_close,
2392 };
2393