xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision 9607e8bc)
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.39 (Berkeley) 01/24/95";
11 #endif /* not lint */
12 
13 #include "sendmail.h"
14 
15 #ifdef NDBM
16 #include <ndbm.h>
17 #endif
18 #ifdef NEWDB
19 #include <db.h>
20 #endif
21 #ifdef NIS
22 #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 #ifdef MAYBENEXTRELEASE
462 		if (aliaswait(map, ".pag", FALSE))
463 			return TRUE;
464 #endif
465 		if (!bitset(MF_OPTIONAL, map->map_mflags))
466 			syserr("Cannot open DBM database %s", map->map_file);
467 		return FALSE;
468 	}
469 	map->map_db1 = (void *) dbm;
470 	if (mode == O_RDONLY)
471 	{
472 		if (bitset(MF_ALIAS, map->map_mflags) &&
473 		    !aliaswait(map, ".pag", TRUE))
474 			return FALSE;
475 	}
476 	else
477 	{
478 		int fd;
479 
480 		/* exclusive lock for duration of rebuild */
481 		fd = dbm_dirfno((DBM *) map->map_db1);
482 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags) &&
483 		    lockfile(fd, map->map_file, ".dir", LOCK_EX))
484 			map->map_mflags |= MF_LOCKED;
485 	}
486 	if (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0)
487 		map->map_mtime = st.st_mtime;
488 	return TRUE;
489 }
490 
491 
492 /*
493 **  DBM_MAP_LOOKUP -- look up a datum in a DBM-type map
494 */
495 
496 char *
497 ndbm_map_lookup(map, name, av, statp)
498 	MAP *map;
499 	char *name;
500 	char **av;
501 	int *statp;
502 {
503 	datum key, val;
504 	int fd;
505 	char keybuf[MAXNAME + 1];
506 
507 	if (tTd(38, 20))
508 		printf("ndbm_map_lookup(%s, %s)\n",
509 			map->map_mname, name);
510 
511 	key.dptr = name;
512 	key.dsize = strlen(name);
513 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
514 	{
515 		if (key.dsize > sizeof keybuf - 1)
516 			key.dsize = sizeof keybuf - 1;
517 		bcopy(key.dptr, keybuf, key.dsize + 1);
518 		makelower(keybuf);
519 		key.dptr = keybuf;
520 	}
521 	fd = dbm_dirfno((DBM *) map->map_db1);
522 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
523 		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
524 	val.dptr = NULL;
525 	if (bitset(MF_TRY0NULL, map->map_mflags))
526 	{
527 		val = dbm_fetch((DBM *) map->map_db1, key);
528 		if (val.dptr != NULL)
529 			map->map_mflags &= ~MF_TRY1NULL;
530 	}
531 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
532 	{
533 		key.dsize++;
534 		val = dbm_fetch((DBM *) map->map_db1, key);
535 		if (val.dptr != NULL)
536 			map->map_mflags &= ~MF_TRY0NULL;
537 	}
538 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
539 		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
540 	if (val.dptr == NULL)
541 		return NULL;
542 	if (bitset(MF_MATCHONLY, map->map_mflags))
543 		return map_rewrite(map, name, strlen(name), NULL);
544 	else
545 		return map_rewrite(map, val.dptr, val.dsize, av);
546 }
547 
548 
549 /*
550 **  DBM_MAP_STORE -- store a datum in the database
551 */
552 
553 void
554 ndbm_map_store(map, lhs, rhs)
555 	register MAP *map;
556 	char *lhs;
557 	char *rhs;
558 {
559 	datum key;
560 	datum data;
561 	int stat;
562 
563 	if (tTd(38, 12))
564 		printf("ndbm_map_store(%s, %s, %s)\n",
565 			map->map_mname, lhs, rhs);
566 
567 	key.dsize = strlen(lhs);
568 	key.dptr = lhs;
569 
570 	data.dsize = strlen(rhs);
571 	data.dptr = rhs;
572 
573 	if (bitset(MF_INCLNULL, map->map_mflags))
574 	{
575 		key.dsize++;
576 		data.dsize++;
577 	}
578 
579 	stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
580 	if (stat > 0)
581 	{
582 		usrerr("050 Warning: duplicate alias name %s", lhs);
583 		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
584 	}
585 	if (stat != 0)
586 		syserr("readaliases: dbm put (%s)", lhs);
587 }
588 
589 
590 /*
591 **  NDBM_MAP_CLOSE -- close the database
592 */
593 
594 void
595 ndbm_map_close(map)
596 	register MAP  *map;
597 {
598 	if (tTd(38, 9))
599 		printf("ndbm_map_close(%s, %s, %x)\n",
600 			map->map_mname, map->map_file, map->map_mflags);
601 
602 	if (bitset(MF_WRITABLE, map->map_mflags))
603 	{
604 #ifdef NIS
605 		bool inclnull;
606 		char buf[200];
607 
608 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
609 		map->map_mflags &= ~MF_INCLNULL;
610 
611 		(void) sprintf(buf, "%010ld", curtime());
612 		ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
613 
614 		(void) gethostname(buf, sizeof buf);
615 		ndbm_map_store(map, "YP_MASTER_NAME", buf);
616 
617 		if (inclnull)
618 			map->map_mflags |= MF_INCLNULL;
619 #endif
620 
621 		/* write out the distinguished alias */
622 		ndbm_map_store(map, "@", "@");
623 	}
624 	dbm_close((DBM *) map->map_db1);
625 }
626 
627 #endif
628 /*
629 **  NEWDB (Hash and BTree) Modules
630 */
631 
632 #ifdef NEWDB
633 
634 /*
635 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
636 **
637 **	These do rather bizarre locking.  If you can lock on open,
638 **	do that to avoid the condition of opening a database that
639 **	is being rebuilt.  If you don't, we'll try to fake it, but
640 **	there will be a race condition.  If opening for read-only,
641 **	we immediately release the lock to avoid freezing things up.
642 **	We really ought to hold the lock, but guarantee that we won't
643 **	be pokey about it.  That's hard to do.
644 */
645 
646 bool
647 bt_map_open(map, mode)
648 	MAP *map;
649 	int mode;
650 {
651 	DB *db;
652 	int i;
653 	int omode;
654 	int fd;
655 	struct stat st;
656 	char buf[MAXNAME];
657 
658 	if (tTd(38, 2))
659 		printf("bt_map_open(%s, %s, %d)\n",
660 			map->map_mname, map->map_file, mode);
661 
662 	omode = mode;
663 	if (omode == O_RDWR)
664 	{
665 		omode |= O_CREAT|O_TRUNC;
666 #if defined(O_EXLOCK) && HASFLOCK
667 		omode |= O_EXLOCK;
668 # if !OLD_NEWDB
669 	}
670 	else
671 	{
672 		omode |= O_SHLOCK;
673 # endif
674 #endif
675 	}
676 
677 	(void) strcpy(buf, map->map_file);
678 	i = strlen(buf);
679 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
680 		(void) strcat(buf, ".db");
681 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
682 	if (db == NULL)
683 	{
684 #ifdef MAYBENEXTRELEASE
685 		if (aliaswait(map, ".db", FALSE))
686 			return TRUE;
687 #endif
688 		if (!bitset(MF_OPTIONAL, map->map_mflags))
689 			syserr("Cannot open BTREE database %s", map->map_file);
690 		return FALSE;
691 	}
692 #if !OLD_NEWDB
693 	fd = db->fd(db);
694 # if HASFLOCK
695 #  if !defined(O_EXLOCK)
696 	if (mode == O_RDWR && fd >= 0)
697 	{
698 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
699 			map->map_mflags |= MF_LOCKED;
700 	}
701 #  else
702 	if (mode == O_RDONLY && fd >= 0)
703 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
704 	else
705 		map->map_mflags |= MF_LOCKED;
706 #  endif
707 # endif
708 #endif
709 
710 	/* try to make sure that at least the database header is on disk */
711 	if (mode == O_RDWR)
712 #if OLD_NEWDB
713 		(void) db->sync(db);
714 #else
715 		(void) db->sync(db, 0);
716 
717 	if (fd >= 0 && fstat(fd, &st) >= 0)
718 		map->map_mtime = st.st_mtime;
719 #endif
720 
721 	map->map_db2 = (void *) db;
722 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
723 		if (!aliaswait(map, ".db", TRUE))
724 			return FALSE;
725 	return TRUE;
726 }
727 
728 
729 /*
730 **  HASH_MAP_INIT -- HASH-style map initialization
731 */
732 
733 bool
734 hash_map_open(map, mode)
735 	MAP *map;
736 	int mode;
737 {
738 	DB *db;
739 	int i;
740 	int omode;
741 	int fd;
742 	struct stat st;
743 	char buf[MAXNAME];
744 
745 	if (tTd(38, 2))
746 		printf("hash_map_open(%s, %s, %d)\n",
747 			map->map_mname, map->map_file, mode);
748 
749 	omode = mode;
750 	if (omode == O_RDWR)
751 	{
752 		omode |= O_CREAT|O_TRUNC;
753 #if defined(O_EXLOCK) && HASFLOCK
754 		omode |= O_EXLOCK;
755 # if !OLD_NEWDB
756 	}
757 	else
758 	{
759 		omode |= O_SHLOCK;
760 # endif
761 #endif
762 	}
763 
764 	(void) strcpy(buf, map->map_file);
765 	i = strlen(buf);
766 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
767 		(void) strcat(buf, ".db");
768 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
769 	if (db == NULL)
770 	{
771 #ifdef MAYBENEXTRELEASE
772 		if (aliaswait(map, ".db", FALSE))
773 			return TRUE;
774 #endif
775 		if (!bitset(MF_OPTIONAL, map->map_mflags))
776 			syserr("Cannot open HASH database %s", map->map_file);
777 		return FALSE;
778 	}
779 #if !OLD_NEWDB
780 	fd = db->fd(db);
781 # if HASFLOCK
782 #  if !defined(O_EXLOCK)
783 	if (mode == O_RDWR && fd >= 0)
784 	{
785 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
786 			map->map_mflags |= MF_LOCKED;
787 	}
788 #  else
789 	if (mode == O_RDONLY && fd >= 0)
790 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
791 	else
792 		map->map_mflags |= MF_LOCKED;
793 #  endif
794 # endif
795 #endif
796 
797 	/* try to make sure that at least the database header is on disk */
798 	if (mode == O_RDWR)
799 #if OLD_NEWDB
800 		(void) db->sync(db);
801 #else
802 		(void) db->sync(db, 0);
803 
804 	if (fd >= 0 && fstat(fd, &st) >= 0)
805 		map->map_mtime = st.st_mtime;
806 #endif
807 
808 	map->map_db2 = (void *) db;
809 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
810 		if (!aliaswait(map, ".db", TRUE))
811 			return FALSE;
812 	return TRUE;
813 }
814 
815 
816 /*
817 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
818 */
819 
820 char *
821 db_map_lookup(map, name, av, statp)
822 	MAP *map;
823 	char *name;
824 	char **av;
825 	int *statp;
826 {
827 	DBT key, val;
828 	register DB *db = (DB *) map->map_db2;
829 	int st;
830 	int saveerrno;
831 	int fd;
832 	char keybuf[MAXNAME + 1];
833 
834 	if (tTd(38, 20))
835 		printf("db_map_lookup(%s, %s)\n",
836 			map->map_mname, name);
837 
838 	key.size = strlen(name);
839 	if (key.size > sizeof keybuf - 1)
840 		key.size = sizeof keybuf - 1;
841 	key.data = keybuf;
842 	bcopy(name, keybuf, key.size + 1);
843 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
844 		makelower(keybuf);
845 #if !OLD_NEWDB
846 	fd = db->fd(db);
847 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
848 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
849 #endif
850 	st = 1;
851 	if (bitset(MF_TRY0NULL, map->map_mflags))
852 	{
853 		st = db->get(db, &key, &val, 0);
854 		if (st == 0)
855 			map->map_mflags &= ~MF_TRY1NULL;
856 	}
857 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
858 	{
859 		key.size++;
860 		st = db->get(db, &key, &val, 0);
861 		if (st == 0)
862 			map->map_mflags &= ~MF_TRY0NULL;
863 	}
864 	saveerrno = errno;
865 #if !OLD_NEWDB
866 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
867 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
868 #endif
869 	if (st != 0)
870 	{
871 		errno = saveerrno;
872 		if (st < 0)
873 			syserr("db_map_lookup: get (%s)", name);
874 		return NULL;
875 	}
876 	if (bitset(MF_MATCHONLY, map->map_mflags))
877 		return map_rewrite(map, name, strlen(name), NULL);
878 	else
879 		return map_rewrite(map, val.data, val.size, av);
880 }
881 
882 
883 /*
884 **  DB_MAP_STORE -- store a datum in the NEWDB database
885 */
886 
887 void
888 db_map_store(map, lhs, rhs)
889 	register MAP *map;
890 	char *lhs;
891 	char *rhs;
892 {
893 	int stat;
894 	DBT key;
895 	DBT data;
896 	register DB *db = map->map_db2;
897 
898 	if (tTd(38, 20))
899 		printf("db_map_store(%s, %s, %s)\n",
900 			map->map_mname, lhs, rhs);
901 
902 	key.size = strlen(lhs);
903 	key.data = lhs;
904 
905 	data.size = strlen(rhs);
906 	data.data = rhs;
907 
908 	if (bitset(MF_INCLNULL, map->map_mflags))
909 	{
910 		key.size++;
911 		data.size++;
912 	}
913 
914 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
915 	if (stat > 0)
916 	{
917 		usrerr("050 Warning: duplicate alias name %s", lhs);
918 		stat = db->put(db, &key, &data, 0);
919 	}
920 	if (stat != 0)
921 		syserr("readaliases: db put (%s)", lhs);
922 }
923 
924 
925 /*
926 **  DB_MAP_CLOSE -- add distinguished entries and close the database
927 */
928 
929 void
930 db_map_close(map)
931 	MAP *map;
932 {
933 	register DB *db = map->map_db2;
934 
935 	if (tTd(38, 9))
936 		printf("db_map_close(%s, %s, %x)\n",
937 			map->map_mname, map->map_file, map->map_mflags);
938 
939 	if (bitset(MF_WRITABLE, map->map_mflags))
940 	{
941 		/* write out the distinguished alias */
942 		db_map_store(map, "@", "@");
943 	}
944 
945 	if (db->close(db) != 0)
946 		syserr("readaliases: db close failure");
947 }
948 
949 #endif
950 /*
951 **  NIS Modules
952 */
953 
954 # ifdef NIS
955 
956 # ifndef YPERR_BUSY
957 #  define YPERR_BUSY	16
958 # endif
959 
960 /*
961 **  NIS_MAP_OPEN -- open DBM map
962 */
963 
964 bool
965 nis_map_open(map, mode)
966 	MAP *map;
967 	int mode;
968 {
969 	int yperr;
970 	register char *p;
971 	auto char *vp;
972 	auto int vsize;
973 	char *master;
974 
975 	if (tTd(38, 2))
976 		printf("nis_map_open(%s, %s)\n",
977 			map->map_mname, map->map_file);
978 
979 	if (mode != O_RDONLY)
980 	{
981 		/* issue a pseudo-error message */
982 #ifdef ENOSYS
983 		errno = ENOSYS;
984 #else
985 # ifdef EFTYPE
986 		errno = EFTYPE;
987 # else
988 		errno = ENXIO;
989 # endif
990 #endif
991 		return FALSE;
992 	}
993 
994 	p = strchr(map->map_file, '@');
995 	if (p != NULL)
996 	{
997 		*p++ = '\0';
998 		if (*p != '\0')
999 			map->map_domain = p;
1000 	}
1001 
1002 	if (*map->map_file == '\0')
1003 		map->map_file = "mail.aliases";
1004 
1005 	if (map->map_domain == NULL)
1006 	{
1007 		yperr = yp_get_default_domain(&map->map_domain);
1008 		if (yperr != 0)
1009 		{
1010 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1011 				syserr("421 NIS map %s specified, but NIS not running\n",
1012 					map->map_file);
1013 			return FALSE;
1014 		}
1015 	}
1016 
1017 	/* check to see if this map actually exists */
1018 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1019 			&vp, &vsize);
1020 	if (tTd(38, 10))
1021 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1022 			map->map_domain, map->map_file, yperr_string(yperr));
1023 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1024 		return TRUE;
1025 
1026 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1027 		syserr("421 Cannot bind to domain %s: %s", map->map_domain,
1028 			yperr_string(yperr));
1029 
1030 	return FALSE;
1031 }
1032 
1033 
1034 /*
1035 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1036 */
1037 
1038 char *
1039 nis_map_lookup(map, name, av, statp)
1040 	MAP *map;
1041 	char *name;
1042 	char **av;
1043 	int *statp;
1044 {
1045 	char *vp;
1046 	auto int vsize;
1047 	int buflen;
1048 	int yperr;
1049 	char keybuf[MAXNAME + 1];
1050 
1051 	if (tTd(38, 20))
1052 		printf("nis_map_lookup(%s, %s)\n",
1053 			map->map_mname, name);
1054 
1055 	buflen = strlen(name);
1056 	if (buflen > sizeof keybuf - 1)
1057 		buflen = sizeof keybuf - 1;
1058 	bcopy(name, keybuf, buflen + 1);
1059 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1060 		makelower(keybuf);
1061 	yperr = YPERR_KEY;
1062 	if (bitset(MF_TRY0NULL, map->map_mflags))
1063 	{
1064 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1065 			     &vp, &vsize);
1066 		if (yperr == 0)
1067 			map->map_mflags &= ~MF_TRY1NULL;
1068 	}
1069 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1070 	{
1071 		buflen++;
1072 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1073 			     &vp, &vsize);
1074 		if (yperr == 0)
1075 			map->map_mflags &= ~MF_TRY0NULL;
1076 	}
1077 	if (yperr != 0)
1078 	{
1079 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1080 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1081 		return NULL;
1082 	}
1083 	if (bitset(MF_MATCHONLY, map->map_mflags))
1084 		return map_rewrite(map, name, strlen(name), NULL);
1085 	else
1086 		return map_rewrite(map, vp, vsize, av);
1087 }
1088 
1089 #endif
1090 /*
1091 **  NISPLUS Modules
1092 **
1093 **	This code donated by Sun Microsystems.
1094 */
1095 
1096 #ifdef NISPLUS
1097 
1098 #undef NIS /* symbol conflict in nis.h */
1099 #include <rpcsvc/nis.h>
1100 #include <rpcsvc/nislib.h>
1101 
1102 #define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1103 #define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1104 #define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1105 #define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
1106 
1107 /*
1108 **  NISPLUS_MAP_OPEN -- open nisplus table
1109 */
1110 
1111 bool
1112 nisplus_map_open(map, mode)
1113 	MAP *map;
1114 	int mode;
1115 {
1116 	register char *p;
1117 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1118 	nis_result *res = NULL;
1119 	u_int objs_len;
1120 	nis_object *obj_ptr;
1121 	int retry_cnt, max_col, i;
1122 
1123 	if (tTd(38, 2))
1124 		printf("nisplus_map_open(%s, %s, %d)\n",
1125 			map->map_mname, map->map_file, mode);
1126 
1127 	if (mode != O_RDONLY)
1128 	{
1129 		errno = ENODEV;
1130 		return FALSE;
1131 	}
1132 
1133 	if (*map->map_file == '\0')
1134 		map->map_file = "mail_aliases.org_dir";
1135 
1136 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1137 	{
1138 		/* set default NISPLUS Domain to $m */
1139 		extern char *nisplus_default_domain();
1140 
1141 		map->map_domain = newstr(nisplus_default_domain());
1142 		if (tTd(38, 2))
1143 			printf("nisplus_map_open(%s): using domain %s\n",
1144 				 map->map_file, map->map_domain);
1145 	}
1146 	if (!PARTIAL_NAME(map->map_file))
1147 		map->map_domain = newstr("");
1148 
1149 	/* check to see if this map actually exists */
1150 	if (PARTIAL_NAME(map->map_file))
1151 		sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
1152 	else
1153 		strcpy(qbuf, map->map_file);
1154 
1155 	retry_cnt = 0;
1156 	while (res == NULL || res->status != NIS_SUCCESS)
1157 	{
1158 		res = nis_lookup(qbuf, FOLLOW_LINKS);
1159 		switch (res->status)
1160 		{
1161 		  case NIS_SUCCESS:
1162 		  case NIS_TRYAGAIN:
1163 		  case NIS_RPCERROR:
1164 		  case NIS_NAMEUNREACHABLE:
1165 			break;
1166 
1167 		  default:		/* all other nisplus errors */
1168 #if 0
1169 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1170 				syserr("421 Cannot find table %s.%s: %s",
1171 					map->map_file, map->map_domain,
1172 					nis_sperrno(res->status));
1173 #endif
1174 			errno = EBADR;
1175 			return FALSE;
1176 		}
1177 		sleep(2);		/* try not to overwhelm hosed server */
1178 		if (retry_cnt++ > 4)
1179 		{
1180 			errno = EBADR;
1181 			return FALSE;
1182 		}
1183 	}
1184 
1185 	if (NIS_RES_NUMOBJ(res) != 1 ||
1186 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1187 	{
1188 		if (tTd(38, 10))
1189 			printf("nisplus_map_open: %s is not a table\n", qbuf);
1190 #if 0
1191 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1192 			syserr("421 %s.%s: %s is not a table",
1193 				map->map_file, map->map_domain,
1194 				nis_sperrno(res->status));
1195 #endif
1196 		errno = EBADR;
1197 		return FALSE;
1198 	}
1199 	/* default key column is column 0 */
1200 	if (map->map_keycolnm == NULL)
1201 		map->map_keycolnm = newstr(COL_NAME(res,0));
1202 
1203 	max_col = COL_MAX(res);
1204 
1205 	/* verify the key column exist */
1206 	for (i=0; i< max_col; i++)
1207 	{
1208 		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
1209 			break;
1210 	}
1211 	if (i == max_col)
1212 	{
1213 		if (tTd(38, 2))
1214 			printf("nisplus_map_open(%s): can not find key column %s\n",
1215 				map->map_file, map->map_keycolnm);
1216 		errno = EBADR;
1217 		return FALSE;
1218 	}
1219 
1220 	/* default value column is the last column */
1221 	if (map->map_valcolnm == NULL)
1222 	{
1223 		map->map_valcolno = max_col - 1;
1224 		return TRUE;
1225 	}
1226 
1227 	for (i=0; i< max_col; i++)
1228 	{
1229 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1230 		{
1231 			map->map_valcolno = i;
1232 			return TRUE;
1233 		}
1234 	}
1235 
1236 	if (tTd(38, 2))
1237 		printf("nisplus_map_open(%s): can not find column %s\n",
1238 			 map->map_file, map->map_keycolnm);
1239 	errno = EBADR;
1240 	return FALSE;
1241 }
1242 
1243 
1244 /*
1245 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1246 */
1247 
1248 char *
1249 nisplus_map_lookup(map, name, av, statp)
1250 	MAP *map;
1251 	char *name;
1252 	char **av;
1253 	int *statp;
1254 {
1255 	char *vp;
1256 	auto int vsize;
1257 	int buflen;
1258 	char search_key[MAXNAME + 1];
1259 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1260 	nis_result *result;
1261 
1262 	if (tTd(38, 20))
1263 		printf("nisplus_map_lookup(%s, %s)\n",
1264 			map->map_mname, name);
1265 
1266 	if (!bitset(MF_OPEN, map->map_mflags))
1267 	{
1268 		if (nisplus_map_open(map, O_RDONLY))
1269 			map->map_mflags |= MF_OPEN;
1270 		else
1271 		{
1272 			*statp = EX_UNAVAILABLE;
1273 			return NULL;
1274 		}
1275 	}
1276 
1277 	buflen = strlen(name);
1278 	if (buflen > sizeof search_key - 1)
1279 		buflen = sizeof search_key - 1;
1280 	bcopy(name, search_key, buflen + 1);
1281 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1282 		makelower(search_key);
1283 
1284 	/* construct the query */
1285 	if (PARTIAL_NAME(map->map_file))
1286 		sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
1287 			search_key, map->map_file, map->map_domain);
1288 	else
1289 		sprintf(qbuf, "[%s=%s],%s", map->map_keycolnm,
1290 			search_key, map->map_file);
1291 
1292 	if (tTd(38, 20))
1293 		printf("qbuf=%s\n", qbuf);
1294 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1295 	if (result->status == NIS_SUCCESS)
1296 	{
1297 		int count;
1298 		char *str;
1299 
1300 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1301 		{
1302 			if (LogLevel > 10)
1303 				syslog(LOG_WARNING,
1304 				  "%s:Lookup error, expected 1 entry, got (%d)",
1305 				    map->map_file, count);
1306 
1307 			/* ignore second entry */
1308 			if (tTd(38, 20))
1309 				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1310 					name, count);
1311 		}
1312 
1313 		vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1314 		/* set the length of the result */
1315 		if (vp == NULL)
1316 			vp = "";
1317 		vsize = strlen(vp);
1318 		if (tTd(38, 20))
1319 			printf("nisplus_map_lookup(%s), found %s\n",
1320 				name, vp);
1321 		if (bitset(MF_MATCHONLY, map->map_mflags))
1322 			str = map_rewrite(map, name, strlen(name), NULL);
1323 		else
1324 			str = map_rewrite(map, vp, vsize, av);
1325 		nis_freeresult(result);
1326 #ifdef MAP_EXIT_STAT
1327 		*statp = EX_OK;
1328 #endif
1329 		return str;
1330 	}
1331 	else
1332 	{
1333 #ifdef MAP_EXIT_STAT
1334 		if (result->status == NIS_NOTFOUND)
1335 			*statp = EX_NOTFOUND;
1336 		else if (result->status == NIS_TRYAGAIN)
1337 			*statp = EX_TEMPFAIL;
1338 		else
1339 		{
1340 			*statp = EX_UNAVAILABLE;
1341 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1342 		}
1343 #else
1344 		if ((result->status != NIS_NOTFOUND) &&
1345 		    (result->status != NIS_TRYAGAIN))
1346 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1347 #endif
1348 	}
1349 	if (tTd(38, 20))
1350 		printf("nisplus_map_lookup(%s), failed\n", name);
1351 	nis_freeresult(result);
1352 	return NULL;
1353 }
1354 
1355 
1356 char *
1357 nisplus_default_domain()
1358 {
1359 	static char default_domain[MAXNAME] = "";
1360 	nis_result *res = NULL;
1361 	char *p;
1362 
1363 	if (default_domain[0] != '\0')
1364 		return(default_domain);
1365 
1366 	if (VendorCode == VENDOR_SUN && ConfigLevel < 2)
1367 	{
1368 		/* for old config, user nis+ local directory        */
1369 		/* have to be backward compatible with bugs too :-( */
1370 		p = nis_local_directory();
1371 		strcpy(default_domain, p);
1372 		return default_domain;
1373 	}
1374 
1375 	if ((p = macvalue('m', CurEnv)) == NULL)
1376 	{
1377 		p = nis_local_directory();
1378 		strcpy(default_domain, p);
1379 		return default_domain;
1380 	}
1381 
1382 	strcpy(default_domain, p);
1383 	if (PARTIAL_NAME(default_domain))
1384 		strcat(default_domain, ".");
1385 
1386 	res = nis_lookup(default_domain, FOLLOW_LINKS);
1387 	if (res->status == NIS_NOTFOUND)
1388 	{
1389 		p = nis_local_directory();
1390 		strcpy(default_domain, p);
1391 	}
1392 	return(default_domain);
1393 }
1394 
1395 #endif /* NISPLUS */
1396 /*
1397 **  HESIOD Modules
1398 **
1399 **	Only works for aliases (for now).
1400 */
1401 
1402 #ifdef HESIOD
1403 
1404 char *
1405 hes_map_lookup(map, name, av, statp)
1406         MAP *map;
1407         char *name;
1408         char **av;
1409         int *statp;
1410 {
1411 	struct hes_postoffice *pobox;
1412 	char keybuf[MAXNAME + 1];
1413 
1414 	if (tTd(38, 20))
1415 		printf("hes_map_lookup(%s)\n", name);
1416 
1417 	pobox = hes_getmailhost(name);
1418 	if (pobox == NULL)
1419 		return NULL;
1420 
1421 	/* We only know how to deal with the SMTP types right now */
1422 	if (strcmp(pobox->po_type, "SMTP") != 0 &&
1423 	    strcmp(pobox->po_type, "ESMTP") != 0)
1424 		return NULL;
1425 
1426 	/* if just checking for a match, we are done */
1427 	if (bitset(MF_MATCHONLY, map->map_mflags))
1428 		return map_rewrite(map, name, strlen(name), NULL);
1429 
1430 	/* Do the rewriting with new values */
1431 	if (strlen(pobox->po_name) + strlen(pobox->po_host) + 1 > sizeof keybuf)
1432 	{
1433 		*statp = EX_DATAERR;
1434 		return NULL;
1435 	}
1436 	(void) sprintf(keybuf, "%s@%s", pobox->po_name, pobox->po_host);
1437 	return map_rewrite(map, keybuf, strlen(keybuf), av);
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;
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 **  Sequenced map type.
2041 **
2042 **	Tries each map in order until something matches, much like
2043 **	implicit.  Stores go to the first map in the list that can
2044 **	support storing.
2045 **
2046 **	This is slightly unusual in that there are two interfaces.
2047 **	The "sequence" interface lets you stack maps arbitrarily.
2048 **	The "switch" interface builds a sequence map by looking
2049 **	at a system-dependent configuration file such as
2050 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2051 **
2052 **	We don't need an explicit open, since all maps are
2053 **	opened during startup, including underlying maps.
2054 */
2055 
2056 /*
2057 **  SEQ_MAP_PARSE -- Sequenced map parsing
2058 */
2059 
2060 bool
2061 seq_map_parse(map, ap)
2062 	MAP *map;
2063 	char *ap;
2064 {
2065 	int maxmap;
2066 
2067 	if (tTd(38, 2))
2068 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2069 	maxmap = 0;
2070 	while (*ap != '\0')
2071 	{
2072 		register char *p;
2073 		STAB *s;
2074 
2075 		/* find beginning of map name */
2076 		while (isascii(*ap) && isspace(*ap))
2077 			ap++;
2078 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2079 			continue;
2080 		if (*p != '\0')
2081 			*p++ = '\0';
2082 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2083 			p++;
2084 		if (*ap == '\0')
2085 		{
2086 			ap = p;
2087 			continue;
2088 		}
2089 		s = stab(ap, ST_MAP, ST_FIND);
2090 		if (s == NULL)
2091 		{
2092 			syserr("Sequence map %s: unknown member map %s",
2093 				map->map_mname, ap);
2094 		}
2095 		else if (maxmap == MAXMAPSTACK)
2096 		{
2097 			syserr("Sequence map %s: too many member maps (%d max)",
2098 				map->map_mname, MAXMAPSTACK);
2099 			maxmap++;
2100 		}
2101 		else if (maxmap < MAXMAPSTACK)
2102 		{
2103 			map->map_stack[maxmap++] = &s->s_map;
2104 		}
2105 		ap = p;
2106 	}
2107 	return TRUE;
2108 }
2109 
2110 
2111 /*
2112 **  SWITCH_MAP_OPEN -- open a switched map
2113 **
2114 **	This looks at the system-dependent configuration and builds
2115 **	a sequence map that does the same thing.
2116 **
2117 **	Every system must define a switch_map_find routine in conf.c
2118 **	that will return the list of service types associated with a
2119 **	given service class.
2120 */
2121 
2122 bool
2123 switch_map_open(map, mode)
2124 	MAP *map;
2125 	int mode;
2126 {
2127 	int mapno;
2128 	int nmaps;
2129 	char *maptype[MAXMAPSTACK];
2130 
2131 	if (tTd(38, 2))
2132 		printf("switch_map_open(%s, %s, %d)\n",
2133 			map->map_mname, map->map_file, mode);
2134 
2135 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2136 	if (tTd(38, 19))
2137 	{
2138 		printf("\tswitch_map_find => %d\n", nmaps);
2139 		for (mapno = 0; mapno < nmaps; mapno++)
2140 			printf("\t\t%s\n", maptype[mapno]);
2141 	}
2142 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2143 		return FALSE;
2144 
2145 	for (mapno = 0; mapno < nmaps; mapno++)
2146 	{
2147 		register STAB *s;
2148 		char nbuf[MAXNAME + 1];
2149 
2150 		if (maptype[mapno] == NULL)
2151 			continue;
2152 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2153 		s = stab(nbuf, ST_MAP, ST_FIND);
2154 		if (s == NULL)
2155 		{
2156 			syserr("Switch map %s: unknown member map %s",
2157 				map->map_mname, nbuf);
2158 		}
2159 		else
2160 		{
2161 			map->map_stack[mapno] = &s->s_map;
2162 			if (tTd(38, 4))
2163 				printf("\tmap_stack[%d] = %s:%s\n",
2164 					mapno, s->s_map.map_class->map_cname,
2165 					nbuf);
2166 		}
2167 	}
2168 	return TRUE;
2169 }
2170 
2171 
2172 /*
2173 **  SEQ_MAP_CLOSE -- close all underlying maps
2174 */
2175 
2176 seq_map_close(map)
2177 	MAP *map;
2178 {
2179 	int mapno;
2180 
2181 	if (tTd(38, 20))
2182 		printf("seq_map_close(%s)\n", map->map_mname);
2183 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2184 	{
2185 		MAP *mm = map->map_stack[mapno];
2186 
2187 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2188 			continue;
2189 		mm->map_class->map_close(mm);
2190 	}
2191 }
2192 
2193 
2194 /*
2195 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2196 */
2197 
2198 char *
2199 seq_map_lookup(map, key, args, pstat)
2200 	MAP *map;
2201 	char *key;
2202 	char **args;
2203 	int *pstat;
2204 {
2205 	int mapno;
2206 	int mapbit = 0x01;
2207 
2208 	if (tTd(38, 20))
2209 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2210 
2211 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2212 	{
2213 		MAP *mm = map->map_stack[mapno];
2214 		int stat = 0;
2215 		char *rv;
2216 
2217 		if (mm == NULL)
2218 			continue;
2219 		if (!bitset(MF_OPEN, mm->map_mflags))
2220 		{
2221 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2222 			{
2223 				*pstat = EX_UNAVAILABLE;
2224 				return NULL;
2225 			}
2226 			continue;
2227 		}
2228 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2229 		if (rv != NULL)
2230 			return rv;
2231 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2232 			return NULL;
2233 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2234 		{
2235 			*pstat = stat;
2236 			return NULL;
2237 		}
2238 	}
2239 	return NULL;
2240 }
2241 
2242 
2243 /*
2244 **  SEQ_MAP_STORE -- sequenced map store
2245 */
2246 
2247 void
2248 seq_map_store(map, key, val)
2249 	MAP *map;
2250 	char *key;
2251 	char *val;
2252 {
2253 	int mapno;
2254 
2255 	if (tTd(38, 12))
2256 		printf("seq_map_store(%s, %s, %s)\n",
2257 			map->map_mname, key, val);
2258 
2259 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2260 	{
2261 		MAP *mm = map->map_stack[mapno];
2262 
2263 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2264 			continue;
2265 
2266 		mm->map_class->map_store(mm, key, val);
2267 		return;
2268 	}
2269 	syserr("seq_map_store(%s, %s, %s): no writable map",
2270 		map->map_mname, key, val);
2271 }
2272 /*
2273 **  NULL stubs
2274 */
2275 
2276 bool
2277 null_map_open(map, mode)
2278 	MAP *map;
2279 	int mode;
2280 {
2281 	return TRUE;
2282 }
2283 
2284 void
2285 null_map_close(map)
2286 	MAP *map;
2287 {
2288 	return;
2289 }
2290 
2291 void
2292 null_map_store(map, key, val)
2293 	MAP *map;
2294 	char *key;
2295 	char *val;
2296 {
2297 	return;
2298 }
2299 
2300 
2301 /*
2302 **  BOGUS stubs
2303 */
2304 
2305 char *
2306 bogus_map_lookup(map, key, args, pstat)
2307 	MAP *map;
2308 	char *key;
2309 	char **args;
2310 	int *pstat;
2311 {
2312 	*pstat = EX_TEMPFAIL;
2313 	return NULL;
2314 }
2315 
2316 MAPCLASS	BogusMapClass =
2317 {
2318 	"bogus-map",		NULL,		0,
2319 	NULL,		bogus_map_lookup,	null_map_store,
2320 	null_map_open,	null_map_close,
2321 };
2322