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