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