xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision e031425c)
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.38 (Berkeley) 12/10/94";
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 	p = strchr(map->map_file, '@');
1466 	if (p != NULL)
1467 	{
1468 		*p++ = '\0';
1469 		if (*p != '\0')
1470 			map->map_domain = p;
1471 	}
1472 	if (*map->map_file == '\0')
1473 		map->map_file = NETINFO_DEFAULT_DIR;
1474 
1475 	if (map->map_domain == NULL)
1476 		map->map_domain = NETINFO_DEFAULT_PROPERTY;
1477 
1478 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1479 		map->map_coldelim = ',';
1480 
1481 	return TRUE;
1482 }
1483 
1484 
1485 /*
1486 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1487 */
1488 
1489 char *
1490 ni_map_lookup(map, name, av, statp)
1491 	MAP *map;
1492 	char *name;
1493 	char **av;
1494 	int *statp;
1495 {
1496 	char *res;
1497 	char *propval;
1498 	extern char *ni_propval();
1499 
1500 	if (tTd(38, 20))
1501 		printf("ni_map_lookup(%s, %s)\n",
1502 			map->map_mname, name);
1503 
1504 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1505 			     map->map_valcolnm, map->map_coldelim);
1506 
1507 	if (propval == NULL)
1508 		return NULL;
1509 
1510 	if (bitset(MF_MATCHONLY, map->map_mflags))
1511 		res = map_rewrite(map, name, strlen(name), NULL);
1512 	else
1513 		res = map_rewrite(map, propval, strlen(propval), av);
1514 	free(propval);
1515 	return res;
1516 }
1517 
1518 #endif
1519 /*
1520 **  TEXT (unindexed text file) Modules
1521 **
1522 **	This code donated by Sun Microsystems.
1523 */
1524 
1525 
1526 /*
1527 **  TEXT_MAP_OPEN -- open text table
1528 */
1529 
1530 bool
1531 text_map_open(map, mode)
1532 	MAP *map;
1533 	int mode;
1534 {
1535 	struct stat sbuf;
1536 
1537 	if (tTd(38, 2))
1538 		printf("text_map_open(%s, %s, %d)\n",
1539 			map->map_mname, map->map_file, mode);
1540 
1541 	if (mode != O_RDONLY)
1542 	{
1543 		errno = ENODEV;
1544 		return FALSE;
1545 	}
1546 
1547 	if (*map->map_file == '\0')
1548 	{
1549 		if (tTd(38, 2))
1550 			printf("text_map_open: file name required\n");
1551 		return FALSE;
1552 	}
1553 
1554 	if (map->map_file[0] != '/')
1555 	{
1556 		if (tTd(38, 2))
1557 			printf("text_map_open(%s): file name must be fully qualified\n",
1558 				map->map_file);
1559 		return FALSE;
1560 	}
1561 	/* check to see if this map actually accessable */
1562 	if (access(map->map_file, R_OK) <0)
1563 		return FALSE;
1564 
1565 	/* check to see if this map actually exist */
1566 	if (stat(map->map_file, &sbuf) <0)
1567 	{
1568 		if (tTd(38, 2))
1569 			printf("text_map_open(%s): can not stat %s\n",
1570 				map->map_file, map->map_file);
1571 		return FALSE;
1572 	}
1573 
1574 	if (!S_ISREG(sbuf.st_mode))
1575 	{
1576 		if (tTd(38, 2))
1577 			printf("text_map_open(%s): %s is not a file\n",
1578 				map->map_file, map->map_file);
1579 		return FALSE;
1580 	}
1581 
1582 	if (map->map_keycolnm == NULL)
1583 		map->map_keycolno = 0;
1584 	else
1585 	{
1586 		if (!isdigit(*map->map_keycolnm))
1587 		{
1588 			if (tTd(38, 2))
1589 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1590 					map->map_file, map->map_keycolnm);
1591 			return FALSE;
1592 		}
1593 		map->map_keycolno = atoi(map->map_keycolnm);
1594 	}
1595 
1596 	if (map->map_valcolnm == NULL)
1597 		map->map_valcolno = 0;
1598 	else
1599 	{
1600 		if (!isdigit(*map->map_valcolnm))
1601 		{
1602 			if (tTd(38, 2))
1603 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1604 					map->map_file, map->map_valcolnm);
1605 			return FALSE;
1606 		}
1607 		map->map_valcolno = atoi(map->map_valcolnm);
1608 	}
1609 
1610 	if (map->map_coldelim == '\0')
1611 		map->map_coldelim = ':';
1612 
1613 	if (tTd(38, 2))
1614 	{
1615 		printf("text_map_open(%s): delimiter = %c\n",
1616 			map->map_file, map->map_coldelim);
1617 	}
1618 
1619 	return TRUE;
1620 }
1621 
1622 
1623 /*
1624 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1625 */
1626 
1627 char *
1628 text_map_lookup(map, name, av, statp)
1629 	MAP *map;
1630 	char *name;
1631 	char **av;
1632 	int *statp;
1633 {
1634 	char *vp;
1635 	auto int vsize;
1636 	int buflen;
1637 	char search_key[MAXNAME + 1];
1638 	char linebuf[MAXLINE];
1639 	FILE *f;
1640 	char buf[MAXNAME+1];
1641 	char delim;
1642 	int key_idx;
1643 	bool found_it;
1644 	extern char *get_column();
1645 
1646 
1647 	found_it = FALSE;
1648 	if (tTd(38, 20))
1649 		printf("text_map_lookup(%s)\n", name);
1650 
1651 	buflen = strlen(name);
1652 	if (buflen > sizeof search_key - 1)
1653 		buflen = sizeof search_key - 1;
1654 	bcopy(name, search_key, buflen + 1);
1655 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1656 		makelower(search_key);
1657 
1658 	f = fopen(map->map_file, "r");
1659 	if (f == NULL)
1660 	{
1661 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1662 		*statp = EX_UNAVAILABLE;
1663 		return NULL;
1664 	}
1665 	key_idx = map->map_keycolno;
1666 	delim = map->map_coldelim;
1667 	while (fgets(linebuf, MAXLINE, f))
1668 	{
1669 		char *lf;
1670 		if (linebuf[0] == '#')
1671 			continue; /* skip comment line */
1672 		if (lf = strchr(linebuf, '\n'))
1673 			*lf = '\0';
1674 		if (!strcasecmp(search_key,
1675 				get_column(linebuf, key_idx, delim, buf)))
1676 		{
1677 			found_it = TRUE;
1678 			break;
1679 		}
1680 	}
1681 	fclose(f);
1682 	if (!found_it)
1683 	{
1684 #ifdef MAP_EXIT_STAT
1685 		*statp = EX_NOTFOUND;
1686 #endif
1687 		return(NULL);
1688 	}
1689 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1690 	vsize = strlen(vp);
1691 #ifdef MAP_EXIT_STAT
1692 	*statp = EX_OK;
1693 #endif
1694 	if (bitset(MF_MATCHONLY, map->map_mflags))
1695 		return map_rewrite(map, name, strlen(name), NULL);
1696 	else
1697 		return map_rewrite(map, vp, vsize, av);
1698 }
1699 /*
1700 **  STAB (Symbol Table) Modules
1701 */
1702 
1703 
1704 /*
1705 **  STAB_MAP_LOOKUP -- look up alias in symbol table
1706 */
1707 
1708 char *
1709 stab_map_lookup(map, name, av, pstat)
1710 	register MAP *map;
1711 	char *name;
1712 	char **av;
1713 	int *pstat;
1714 {
1715 	register STAB *s;
1716 
1717 	if (tTd(38, 20))
1718 		printf("stab_lookup(%s, %s)\n",
1719 			map->map_mname, name);
1720 
1721 	s = stab(name, ST_ALIAS, ST_FIND);
1722 	if (s != NULL)
1723 		return (s->s_alias);
1724 	return (NULL);
1725 }
1726 
1727 
1728 /*
1729 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1730 */
1731 
1732 void
1733 stab_map_store(map, lhs, rhs)
1734 	register MAP *map;
1735 	char *lhs;
1736 	char *rhs;
1737 {
1738 	register STAB *s;
1739 
1740 	s = stab(lhs, ST_ALIAS, ST_ENTER);
1741 	s->s_alias = newstr(rhs);
1742 }
1743 
1744 
1745 /*
1746 **  STAB_MAP_OPEN -- initialize (reads data file)
1747 **
1748 **	This is a wierd case -- it is only intended as a fallback for
1749 **	aliases.  For this reason, opens for write (only during a
1750 **	"newaliases") always fails, and opens for read open the
1751 **	actual underlying text file instead of the database.
1752 */
1753 
1754 bool
1755 stab_map_open(map, mode)
1756 	register MAP *map;
1757 	int mode;
1758 {
1759 	FILE *af;
1760 	struct stat st;
1761 
1762 	if (tTd(38, 2))
1763 		printf("stab_map_open(%s, %s)\n",
1764 			map->map_mname, map->map_file);
1765 
1766 	if (mode != O_RDONLY)
1767 	{
1768 		errno = ENODEV;
1769 		return FALSE;
1770 	}
1771 
1772 	af = fopen(map->map_file, "r");
1773 	if (af == NULL)
1774 		return FALSE;
1775 	readaliases(map, af, FALSE, FALSE);
1776 
1777 	if (fstat(fileno(af), &st) >= 0)
1778 		map->map_mtime = st.st_mtime;
1779 	fclose(af);
1780 
1781 	return TRUE;
1782 }
1783 /*
1784 **  Implicit Modules
1785 **
1786 **	Tries several types.  For back compatibility of aliases.
1787 */
1788 
1789 
1790 /*
1791 **  IMPL_MAP_LOOKUP -- lookup in best open database
1792 */
1793 
1794 char *
1795 impl_map_lookup(map, name, av, pstat)
1796 	MAP *map;
1797 	char *name;
1798 	char **av;
1799 	int *pstat;
1800 {
1801 	if (tTd(38, 20))
1802 		printf("impl_map_lookup(%s, %s)\n",
1803 			map->map_mname, name);
1804 
1805 #ifdef NEWDB
1806 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1807 		return db_map_lookup(map, name, av, pstat);
1808 #endif
1809 #ifdef NDBM
1810 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1811 		return ndbm_map_lookup(map, name, av, pstat);
1812 #endif
1813 	return stab_map_lookup(map, name, av, pstat);
1814 }
1815 
1816 /*
1817 **  IMPL_MAP_STORE -- store in open databases
1818 */
1819 
1820 void
1821 impl_map_store(map, lhs, rhs)
1822 	MAP *map;
1823 	char *lhs;
1824 	char *rhs;
1825 {
1826 #ifdef NEWDB
1827 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1828 		db_map_store(map, lhs, rhs);
1829 #endif
1830 #ifdef NDBM
1831 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1832 		ndbm_map_store(map, lhs, rhs);
1833 #endif
1834 	stab_map_store(map, lhs, rhs);
1835 }
1836 
1837 /*
1838 **  IMPL_MAP_OPEN -- implicit database open
1839 */
1840 
1841 bool
1842 impl_map_open(map, mode)
1843 	MAP *map;
1844 	int mode;
1845 {
1846 	struct stat stb;
1847 
1848 	if (tTd(38, 2))
1849 		printf("impl_map_open(%s, %s, %d)\n",
1850 			map->map_mname, map->map_file, mode);
1851 
1852 	if (stat(map->map_file, &stb) < 0)
1853 	{
1854 		/* no alias file at all */
1855 		if (tTd(38, 3))
1856 			printf("no map file\n");
1857 		return FALSE;
1858 	}
1859 
1860 #ifdef NEWDB
1861 	map->map_mflags |= MF_IMPL_HASH;
1862 	if (hash_map_open(map, mode))
1863 	{
1864 #if defined(NDBM) && defined(NIS)
1865 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
1866 #endif
1867 			return TRUE;
1868 	}
1869 	else
1870 		map->map_mflags &= ~MF_IMPL_HASH;
1871 #endif
1872 #ifdef NDBM
1873 	map->map_mflags |= MF_IMPL_NDBM;
1874 	if (ndbm_map_open(map, mode))
1875 	{
1876 		return TRUE;
1877 	}
1878 	else
1879 		map->map_mflags &= ~MF_IMPL_NDBM;
1880 #endif
1881 
1882 #if defined(NEWDB) || defined(NDBM)
1883 	if (Verbose)
1884 		message("WARNING: cannot open alias database %s", map->map_file);
1885 #else
1886 	if (mode != O_RDONLY)
1887 		usrerr("Cannot rebuild aliases: no database format defined");
1888 #endif
1889 
1890 	return stab_map_open(map, mode);
1891 }
1892 
1893 
1894 /*
1895 **  IMPL_MAP_CLOSE -- close any open database(s)
1896 */
1897 
1898 void
1899 impl_map_close(map)
1900 	MAP *map;
1901 {
1902 	if (tTd(38, 20))
1903 		printf("impl_map_close(%s, %s, %x)\n",
1904 			map->map_mname, map->map_file, map->map_mflags);
1905 #ifdef NEWDB
1906 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1907 	{
1908 		db_map_close(map);
1909 		map->map_mflags &= ~MF_IMPL_HASH;
1910 	}
1911 #endif
1912 
1913 #ifdef NDBM
1914 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1915 	{
1916 		ndbm_map_close(map);
1917 		map->map_mflags &= ~MF_IMPL_NDBM;
1918 	}
1919 #endif
1920 }
1921 /*
1922 **  User map class.
1923 **
1924 **	Provides access to the system password file.
1925 */
1926 
1927 /*
1928 **  USER_MAP_OPEN -- open user map
1929 **
1930 **	Really just binds field names to field numbers.
1931 */
1932 
1933 bool
1934 user_map_open(map, mode)
1935 	MAP *map;
1936 	int mode;
1937 {
1938 	if (tTd(38, 2))
1939 		printf("user_map_open(%s)\n", map->map_mname);
1940 
1941 	if (mode != O_RDONLY)
1942 	{
1943 		/* issue a pseudo-error message */
1944 #ifdef ENOSYS
1945 		errno = ENOSYS;
1946 #else
1947 # ifdef EFTYPE
1948 		errno = EFTYPE;
1949 # else
1950 		errno = ENXIO;
1951 # endif
1952 #endif
1953 		return FALSE;
1954 	}
1955 	if (map->map_valcolnm == NULL)
1956 		/* nothing */ ;
1957 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
1958 		map->map_valcolno = 1;
1959 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
1960 		map->map_valcolno = 2;
1961 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
1962 		map->map_valcolno = 3;
1963 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
1964 		map->map_valcolno = 4;
1965 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
1966 		map->map_valcolno = 5;
1967 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
1968 		map->map_valcolno = 6;
1969 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
1970 		map->map_valcolno = 7;
1971 	else
1972 	{
1973 		syserr("User map %s: unknown column name %s",
1974 			map->map_mname, map->map_valcolnm);
1975 		return FALSE;
1976 	}
1977 	return TRUE;
1978 }
1979 
1980 
1981 /*
1982 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
1983 */
1984 
1985 #include <pwd.h>
1986 
1987 char *
1988 user_map_lookup(map, key, av, statp)
1989 	MAP *map;
1990 	char *key;
1991 	char **av;
1992 	int *statp;
1993 {
1994 	struct passwd *pw;
1995 
1996 	if (tTd(38, 20))
1997 		printf("user_map_lookup(%s, %s)\n",
1998 			map->map_mname, key);
1999 
2000 	pw = getpwnam(key);
2001 	if (pw == NULL)
2002 		return NULL;
2003 	if (bitset(MF_MATCHONLY, map->map_mflags))
2004 		return map_rewrite(map, key, strlen(key), NULL);
2005 	else
2006 	{
2007 		char *rwval;
2008 		char buf[30];
2009 
2010 		switch (map->map_valcolno)
2011 		{
2012 		  case 0:
2013 		  case 1:
2014 			rwval = pw->pw_name;
2015 			break;
2016 
2017 		  case 2:
2018 			rwval = pw->pw_passwd;
2019 			break;
2020 
2021 		  case 3:
2022 			sprintf(buf, "%d", pw->pw_uid);
2023 			rwval = buf;
2024 			break;
2025 
2026 		  case 4:
2027 			sprintf(buf, "%d", pw->pw_gid);
2028 			rwval = buf;
2029 			break;
2030 
2031 		  case 5:
2032 			rwval = pw->pw_gecos;
2033 			break;
2034 
2035 		  case 6:
2036 			rwval = pw->pw_dir;
2037 			break;
2038 
2039 		  case 7:
2040 			rwval = pw->pw_shell;
2041 			break;
2042 		}
2043 		return map_rewrite(map, rwval, strlen(rwval), av);
2044 	}
2045 }
2046 /*
2047 **  Sequenced map type.
2048 **
2049 **	Tries each map in order until something matches, much like
2050 **	implicit.  Stores go to the first map in the list that can
2051 **	support storing.
2052 **
2053 **	This is slightly unusual in that there are two interfaces.
2054 **	The "sequence" interface lets you stack maps arbitrarily.
2055 **	The "switch" interface builds a sequence map by looking
2056 **	at a system-dependent configuration file such as
2057 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2058 **
2059 **	We don't need an explicit open, since all maps are
2060 **	opened during startup, including underlying maps.
2061 */
2062 
2063 /*
2064 **  SEQ_MAP_PARSE -- Sequenced map parsing
2065 */
2066 
2067 bool
2068 seq_map_parse(map, ap)
2069 	MAP *map;
2070 	char *ap;
2071 {
2072 	int maxmap;
2073 
2074 	if (tTd(38, 2))
2075 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2076 	maxmap = 0;
2077 	while (*ap != '\0')
2078 	{
2079 		register char *p;
2080 		STAB *s;
2081 
2082 		/* find beginning of map name */
2083 		while (isascii(*ap) && isspace(*ap))
2084 			ap++;
2085 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2086 			continue;
2087 		if (*p != '\0')
2088 			*p++ = '\0';
2089 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2090 			p++;
2091 		if (*ap == '\0')
2092 		{
2093 			ap = p;
2094 			continue;
2095 		}
2096 		s = stab(ap, ST_MAP, ST_FIND);
2097 		if (s == NULL)
2098 		{
2099 			syserr("Sequence map %s: unknown member map %s",
2100 				map->map_mname, ap);
2101 		}
2102 		else if (maxmap == MAXMAPSTACK)
2103 		{
2104 			syserr("Sequence map %s: too many member maps (%d max)",
2105 				map->map_mname, MAXMAPSTACK);
2106 			maxmap++;
2107 		}
2108 		else if (maxmap < MAXMAPSTACK)
2109 		{
2110 			map->map_stack[maxmap++] = &s->s_map;
2111 		}
2112 		ap = p;
2113 	}
2114 	return TRUE;
2115 }
2116 
2117 
2118 /*
2119 **  SWITCH_MAP_OPEN -- open a switched map
2120 **
2121 **	This looks at the system-dependent configuration and builds
2122 **	a sequence map that does the same thing.
2123 **
2124 **	Every system must define a switch_map_find routine in conf.c
2125 **	that will return the list of service types associated with a
2126 **	given service class.
2127 */
2128 
2129 bool
2130 switch_map_open(map, mode)
2131 	MAP *map;
2132 	int mode;
2133 {
2134 	int mapno;
2135 	int nmaps;
2136 	char *maptype[MAXMAPSTACK];
2137 
2138 	if (tTd(38, 2))
2139 		printf("switch_map_open(%s, %s, %d)\n",
2140 			map->map_mname, map->map_file, mode);
2141 
2142 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2143 	if (tTd(38, 19))
2144 	{
2145 		printf("\tswitch_map_find => %d\n", nmaps);
2146 		for (mapno = 0; mapno < nmaps; mapno++)
2147 			printf("\t\t%s\n", maptype[mapno]);
2148 	}
2149 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2150 		return FALSE;
2151 
2152 	for (mapno = 0; mapno < nmaps; mapno++)
2153 	{
2154 		register STAB *s;
2155 		char nbuf[MAXNAME + 1];
2156 
2157 		if (maptype[mapno] == NULL)
2158 			continue;
2159 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2160 		s = stab(nbuf, ST_MAP, ST_FIND);
2161 		if (s == NULL)
2162 		{
2163 			syserr("Switch map %s: unknown member map %s",
2164 				map->map_mname, nbuf);
2165 		}
2166 		else
2167 		{
2168 			map->map_stack[mapno] = &s->s_map;
2169 			if (tTd(38, 4))
2170 				printf("\tmap_stack[%d] = %s:%s\n",
2171 					mapno, s->s_map.map_class->map_cname,
2172 					nbuf);
2173 		}
2174 	}
2175 	return TRUE;
2176 }
2177 
2178 
2179 /*
2180 **  SEQ_MAP_CLOSE -- close all underlying maps
2181 */
2182 
2183 seq_map_close(map)
2184 	MAP *map;
2185 {
2186 	int mapno;
2187 
2188 	if (tTd(38, 20))
2189 		printf("seq_map_close(%s)\n", map->map_mname);
2190 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2191 	{
2192 		MAP *mm = map->map_stack[mapno];
2193 
2194 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2195 			continue;
2196 		mm->map_class->map_close(mm);
2197 	}
2198 }
2199 
2200 
2201 /*
2202 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2203 */
2204 
2205 char *
2206 seq_map_lookup(map, key, args, pstat)
2207 	MAP *map;
2208 	char *key;
2209 	char **args;
2210 	int *pstat;
2211 {
2212 	int mapno;
2213 	int mapbit = 0x01;
2214 
2215 	if (tTd(38, 20))
2216 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2217 
2218 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2219 	{
2220 		MAP *mm = map->map_stack[mapno];
2221 		int stat = 0;
2222 		char *rv;
2223 
2224 		if (mm == NULL)
2225 			continue;
2226 		if (!bitset(MF_OPEN, mm->map_mflags))
2227 		{
2228 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2229 			{
2230 				*pstat = EX_UNAVAILABLE;
2231 				return NULL;
2232 			}
2233 			continue;
2234 		}
2235 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2236 		if (rv != NULL)
2237 			return rv;
2238 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2239 			return NULL;
2240 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2241 		{
2242 			*pstat = stat;
2243 			return NULL;
2244 		}
2245 	}
2246 	return NULL;
2247 }
2248 
2249 
2250 /*
2251 **  SEQ_MAP_STORE -- sequenced map store
2252 */
2253 
2254 void
2255 seq_map_store(map, key, val)
2256 	MAP *map;
2257 	char *key;
2258 	char *val;
2259 {
2260 	int mapno;
2261 
2262 	if (tTd(38, 12))
2263 		printf("seq_map_store(%s, %s, %s)\n",
2264 			map->map_mname, key, val);
2265 
2266 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2267 	{
2268 		MAP *mm = map->map_stack[mapno];
2269 
2270 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2271 			continue;
2272 
2273 		mm->map_class->map_store(mm, key, val);
2274 		return;
2275 	}
2276 	syserr("seq_map_store(%s, %s, %s): no writable map",
2277 		map->map_mname, key, val);
2278 }
2279 /*
2280 **  NULL stubs
2281 */
2282 
2283 bool
2284 null_map_open(map, mode)
2285 	MAP *map;
2286 	int mode;
2287 {
2288 	return TRUE;
2289 }
2290 
2291 void
2292 null_map_close(map)
2293 	MAP *map;
2294 {
2295 	return;
2296 }
2297 
2298 void
2299 null_map_store(map, key, val)
2300 	MAP *map;
2301 	char *key;
2302 	char *val;
2303 {
2304 	return;
2305 }
2306 
2307 
2308 /*
2309 **  BOGUS stubs
2310 */
2311 
2312 char *
2313 bogus_map_lookup(map, key, args, pstat)
2314 	MAP *map;
2315 	char *key;
2316 	char **args;
2317 	int *pstat;
2318 {
2319 	*pstat = EX_TEMPFAIL;
2320 	return NULL;
2321 }
2322 
2323 MAPCLASS	BogusMapClass =
2324 {
2325 	"bogus-map",		NULL,		0,
2326 	NULL,		bogus_map_lookup,	null_map_store,
2327 	null_map_open,	null_map_close,
2328 };
2329