xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision 6b3ba46c)
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.58 (Berkeley) 05/13/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 buf1[MAXLINE + NIS_MAXNAMELEN];
1533 	static char *nis_path = NULL;
1534 	static char nis_path_buf[MAXLINE];
1535 	nis_result *result;
1536 	char *p;
1537 	int len;
1538 
1539 	if (nis_path == NULL)
1540 	{
1541 		char *ptr;
1542 		char pathbuf[MAXLINE];
1543 
1544 		strcpy(buf1, macvalue('m', CurEnv));
1545 		strcat(buf1, ".");
1546 		/* try the host tabe in $m */
1547 		if (hosts_table_ok(buf1))
1548 			strcpy(pathbuf, buf1);
1549 		else
1550 			strcpy(pathbuf, "$");
1551 
1552 		nis_path = nis_path_buf;
1553 		sprintf(nis_path, "NIS_PATH=%s", pathbuf);
1554 	}
1555 
1556 	if (nis_path[0] != '\0')
1557 		putenv(nis_path);
1558 	else
1559 		syslog(LOG_WARNING, "no NIS+ path defined");
1560 
1561 	shorten_hostname(name);
1562 
1563 	p = strchr(name, '.');
1564 	if (p == NULL)
1565 	{
1566 		/* single token */
1567 		sprintf(buf1, "[name=%s],hosts.org_dir", name);
1568 	}
1569 	else if (p[1] != '\0')
1570 	{
1571 		/* multi token -- take only first token in name buf */
1572 		*p = '\0';
1573 		sprintf(buf1, "[name=%s],hosts.org_dir.%s", name, &p[1]);
1574 	}
1575 	else
1576 	{
1577 		*statp = EX_NOHOST;
1578 		return FALSE;
1579 	}
1580 
1581 	if (tTd(38, 20))
1582 		printf("\nnisplus_getcanoname(%s), qbuf=%s\n%s\n",
1583 			 name, buf1, nis_path);
1584 
1585 	result = nis_list(buf1, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
1586 		NULL, NULL);
1587 
1588 	/* unset NIS_PATH, just in case */
1589 	unsetenv("NIS_PATH");
1590 
1591 	if (result->status == NIS_SUCCESS)
1592 	{
1593 		int count;
1594 		char *str;
1595 		char *domain;
1596 
1597 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1598 		{
1599 #ifdef LOG
1600 			if (LogLevel > 10)
1601 				syslog(LOG_WARNING,
1602 				       "nisplus_getcanonname: Lookup error, expected 1 entry, got (%d)",
1603 				       count);
1604 #endif
1605 
1606 			/* ignore second entry */
1607 			if (tTd(38, 20))
1608 				printf("nisplus_getcanoname(%s), got %d entries, addtional entries ignores\n", name);
1609 		}
1610 
1611 		if (tTd(38, 20))
1612 			printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
1613 			       name, (NIS_RES_OBJECT(result))->zo_domain);
1614 
1615 
1616 		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
1617 		vsize = strlen(vp);
1618 		if (tTd(38, 20))
1619 			printf("nisplus_getcanonname(%s), found %s\n",
1620 				name, vp);
1621 		domain = macvalue('m', CurEnv);
1622 		if (hbsize > (vsize + ((int) strlen(domain))))
1623 		{
1624 			sprintf(name, "%s.%s", vp, domain);
1625 			*statp = EX_OK;
1626 		}
1627 		else
1628 			*statp = EX_NOHOST;
1629 		nis_freeresult(result);
1630 		return TRUE;
1631 	}
1632 	else
1633 	{
1634 		if (result->status == NIS_NOTFOUND)
1635 			*statp = EX_NOHOST;
1636 		else if (result->status == NIS_TRYAGAIN)
1637 			*statp = EX_TEMPFAIL;
1638 		else
1639 		{
1640 			*statp = EX_UNAVAILABLE;
1641 		}
1642 	}
1643 	if (tTd(38, 20))
1644 		printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
1645 			name, result->status, *statp);
1646 	nis_freeresult(result);
1647 	return FALSE;
1648 }
1649 
1650 
1651 char *
1652 nisplus_default_domain()
1653 {
1654 	static char default_domain[MAXNAME + 1] = "";
1655 	char *p;
1656 
1657 	if (default_domain[0] != '\0')
1658 		return(default_domain);
1659 
1660 	p = nis_local_directory();
1661 	strcpy(default_domain, p);
1662 	return default_domain;
1663 }
1664 
1665 #endif /* NISPLUS */
1666 /*
1667 **  HESIOD Modules
1668 */
1669 
1670 #ifdef HESIOD
1671 
1672 #include <hesiod.h>
1673 
1674 char *
1675 hes_map_lookup(map, name, av, statp)
1676         MAP *map;
1677         char *name;
1678         char **av;
1679         int *statp;
1680 {
1681 	char **hp;
1682 	char *retdata = NULL;
1683 	int i;
1684 
1685 	if (tTd(38, 20))
1686 		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1687 
1688 	hp = hes_resolve(name, map->map_file);
1689 	if (hp == NULL)
1690 		return NULL;
1691 
1692 	if (hp[0] != NULL)
1693 	{
1694 		if (bitset(MF_MATCHONLY, map->map_mflags))
1695 			retdata = map_rewrite(map, name, strlen(name), NULL);
1696 		else
1697 			retdata = map_rewrite(map, hp[0], strlen(hp[0]), av);
1698 	}
1699 
1700 	for (i = 0; hp[i] != NULL; i++)
1701 		free(hp[i]);
1702 	free(hp);
1703 	return retdata;
1704 }
1705 
1706 #endif
1707 /*
1708 **  NeXT NETINFO Modules
1709 */
1710 
1711 #ifdef NETINFO
1712 
1713 #define NETINFO_DEFAULT_DIR		"/aliases"
1714 #define NETINFO_DEFAULT_PROPERTY	"members"
1715 
1716 
1717 /*
1718 **  NI_MAP_OPEN -- open NetInfo Aliases
1719 */
1720 
1721 bool
1722 ni_map_open(map, mode)
1723 	MAP *map;
1724 	int mode;
1725 {
1726 	char *p;
1727 
1728 	if (tTd(38, 20))
1729 		printf("ni_map_open: %s\n", map->map_file);
1730 
1731 	if (*map->map_file == '\0')
1732 		map->map_file = NETINFO_DEFAULT_DIR;
1733 
1734 	if (map->map_valcolnm == NULL)
1735 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1736 
1737 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1738 		map->map_coldelim = ',';
1739 
1740 	return TRUE;
1741 }
1742 
1743 
1744 /*
1745 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1746 */
1747 
1748 char *
1749 ni_map_lookup(map, name, av, statp)
1750 	MAP *map;
1751 	char *name;
1752 	char **av;
1753 	int *statp;
1754 {
1755 	char *res;
1756 	char *propval;
1757 	extern char *ni_propval();
1758 
1759 	if (tTd(38, 20))
1760 		printf("ni_map_lookup(%s, %s)\n",
1761 			map->map_mname, name);
1762 
1763 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1764 			     map->map_valcolnm, map->map_coldelim);
1765 
1766 	if (propval == NULL)
1767 		return NULL;
1768 
1769 	if (bitset(MF_MATCHONLY, map->map_mflags))
1770 		res = map_rewrite(map, name, strlen(name), NULL);
1771 	else
1772 		res = map_rewrite(map, propval, strlen(propval), av);
1773 	free(propval);
1774 	return res;
1775 }
1776 
1777 #endif
1778 /*
1779 **  TEXT (unindexed text file) Modules
1780 **
1781 **	This code donated by Sun Microsystems.
1782 */
1783 
1784 
1785 /*
1786 **  TEXT_MAP_OPEN -- open text table
1787 */
1788 
1789 bool
1790 text_map_open(map, mode)
1791 	MAP *map;
1792 	int mode;
1793 {
1794 	struct stat sbuf;
1795 
1796 	if (tTd(38, 2))
1797 		printf("text_map_open(%s, %s, %d)\n",
1798 			map->map_mname, map->map_file, mode);
1799 
1800 	if (mode != O_RDONLY)
1801 	{
1802 		errno = ENODEV;
1803 		return FALSE;
1804 	}
1805 
1806 	if (*map->map_file == '\0')
1807 	{
1808 		if (tTd(38, 2))
1809 			printf("text_map_open: file name required\n");
1810 		return FALSE;
1811 	}
1812 
1813 	if (map->map_file[0] != '/')
1814 	{
1815 		if (tTd(38, 2))
1816 			printf("text_map_open(%s): file name must be fully qualified\n",
1817 				map->map_file);
1818 		return FALSE;
1819 	}
1820 	/* check to see if this map actually accessable */
1821 	if (access(map->map_file, R_OK) <0)
1822 		return FALSE;
1823 
1824 	/* check to see if this map actually exist */
1825 	if (stat(map->map_file, &sbuf) <0)
1826 	{
1827 		if (tTd(38, 2))
1828 			printf("text_map_open(%s): can not stat %s\n",
1829 				map->map_file, map->map_file);
1830 		return FALSE;
1831 	}
1832 
1833 	if (!S_ISREG(sbuf.st_mode))
1834 	{
1835 		if (tTd(38, 2))
1836 			printf("text_map_open(%s): %s is not a file\n",
1837 				map->map_file, map->map_file);
1838 		return FALSE;
1839 	}
1840 
1841 	if (map->map_keycolnm == NULL)
1842 		map->map_keycolno = 0;
1843 	else
1844 	{
1845 		if (!isdigit(*map->map_keycolnm))
1846 		{
1847 			if (tTd(38, 2))
1848 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1849 					map->map_file, map->map_keycolnm);
1850 			return FALSE;
1851 		}
1852 		map->map_keycolno = atoi(map->map_keycolnm);
1853 	}
1854 
1855 	if (map->map_valcolnm == NULL)
1856 		map->map_valcolno = 0;
1857 	else
1858 	{
1859 		if (!isdigit(*map->map_valcolnm))
1860 		{
1861 			if (tTd(38, 2))
1862 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1863 					map->map_file, map->map_valcolnm);
1864 			return FALSE;
1865 		}
1866 		map->map_valcolno = atoi(map->map_valcolnm);
1867 	}
1868 
1869 	if (tTd(38, 2))
1870 	{
1871 		printf("text_map_open(%s): delimiter = ",
1872 			map->map_file);
1873 		if (map->map_coldelim == '\0')
1874 			printf("(white space)\n");
1875 		else
1876 			printf("%c\n", map->map_coldelim);
1877 	}
1878 
1879 	return TRUE;
1880 }
1881 
1882 
1883 /*
1884 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1885 */
1886 
1887 char *
1888 text_map_lookup(map, name, av, statp)
1889 	MAP *map;
1890 	char *name;
1891 	char **av;
1892 	int *statp;
1893 {
1894 	char *vp;
1895 	auto int vsize;
1896 	int buflen;
1897 	char search_key[MAXNAME + 1];
1898 	char linebuf[MAXLINE];
1899 	FILE *f;
1900 	char buf[MAXNAME + 1];
1901 	char delim;
1902 	int key_idx;
1903 	bool found_it;
1904 	extern char *get_column();
1905 
1906 
1907 	found_it = FALSE;
1908 	if (tTd(38, 20))
1909 		printf("text_map_lookup(%s)\n", name);
1910 
1911 	buflen = strlen(name);
1912 	if (buflen > sizeof search_key - 1)
1913 		buflen = sizeof search_key - 1;
1914 	bcopy(name, search_key, buflen + 1);
1915 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1916 		makelower(search_key);
1917 
1918 	f = fopen(map->map_file, "r");
1919 	if (f == NULL)
1920 	{
1921 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1922 		*statp = EX_UNAVAILABLE;
1923 		return NULL;
1924 	}
1925 	key_idx = map->map_keycolno;
1926 	delim = map->map_coldelim;
1927 	while (fgets(linebuf, MAXLINE, f))
1928 	{
1929 		char *lf;
1930 		if (linebuf[0] == '#')
1931 			continue; /* skip comment line */
1932 		if (lf = strchr(linebuf, '\n'))
1933 			*lf = '\0';
1934 		if (!strcasecmp(search_key,
1935 				get_column(linebuf, key_idx, delim, buf)))
1936 		{
1937 			found_it = TRUE;
1938 			break;
1939 		}
1940 	}
1941 	fclose(f);
1942 	if (!found_it)
1943 	{
1944 #ifdef MAP_EXIT_STAT
1945 		*statp = EX_NOTFOUND;
1946 #endif
1947 		return(NULL);
1948 	}
1949 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1950 	vsize = strlen(vp);
1951 #ifdef MAP_EXIT_STAT
1952 	*statp = EX_OK;
1953 #endif
1954 	if (bitset(MF_MATCHONLY, map->map_mflags))
1955 		return map_rewrite(map, name, strlen(name), NULL);
1956 	else
1957 		return map_rewrite(map, vp, vsize, av);
1958 }
1959 
1960 
1961 /*
1962 **  TEXT_GETCANONNAME -- look up canonical name in hosts file
1963 */
1964 
1965 bool
1966 text_getcanonname(name, hbsize, statp)
1967 	char *name;
1968 	int hbsize;
1969 	int *statp;
1970 {
1971 	int buflen;
1972 	char delim;
1973 	int key_idx;
1974 	char *cname;
1975 	bool found;
1976 	char *domain;
1977 	FILE *f;
1978 	char linebuf[MAXLINE];
1979 	char cbuf[MAXNAME + 1];
1980 	char buf[MAXNAME + 1];
1981 	extern char *get_column();
1982 
1983 	shorten_hostname(name);
1984 
1985 	/* we only accept single token search key */
1986 	if (strchr(name, '.') != NULL)
1987 	{
1988 		*statp = EX_NOHOST;
1989 		return FALSE;
1990 	}
1991 
1992 	found = FALSE;
1993 
1994 	f = fopen(HostsFile, "r");
1995 	if (f == NULL)
1996 	{
1997 #ifdef MAP_EXIT_STAT
1998 		*statp = EX_UNAVAILABLE;
1999 #endif
2000 		return FALSE;
2001 	}
2002 	delim = '\t';
2003 	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
2004 	{
2005 		char *p;
2006 
2007 		if (linebuf[0] == '#')
2008 			continue;
2009 		if ((p = strchr(linebuf, '\n')) != NULL)
2010 			*p = '\0';
2011 		cname = get_column(linebuf, 1, delim, cbuf);
2012 		if (cname != NULL && strcasecmp(name,  cname) == 0)
2013 		{
2014 			found = TRUE;
2015 			break;
2016 		}
2017 
2018 		key_idx = 2;
2019 		while ((p = get_column(linebuf, key_idx, delim, buf)) != NULL)
2020 		{
2021 			if (strcasecmp(name, p) == 0)
2022 			{
2023 				found = TRUE;
2024 				break;
2025 			}
2026 			key_idx++;
2027 		}
2028 	}
2029 	fclose(f);
2030 	if (!found)
2031 	{
2032 		*statp = EX_NOHOST;
2033 		return FALSE;
2034 	}
2035 
2036 	if (hbsize >= strlen(cname))
2037 	{
2038 		strcpy(name, cname);
2039 		*statp = EX_OK;
2040 		return TRUE;
2041 	}
2042 	*statp = EX_UNAVAILABLE;
2043 	return FALSE;
2044 }
2045 /*
2046 **  STAB (Symbol Table) Modules
2047 */
2048 
2049 
2050 /*
2051 **  STAB_MAP_LOOKUP -- look up alias in symbol table
2052 */
2053 
2054 char *
2055 stab_map_lookup(map, name, av, pstat)
2056 	register MAP *map;
2057 	char *name;
2058 	char **av;
2059 	int *pstat;
2060 {
2061 	register STAB *s;
2062 
2063 	if (tTd(38, 20))
2064 		printf("stab_lookup(%s, %s)\n",
2065 			map->map_mname, name);
2066 
2067 	s = stab(name, ST_ALIAS, ST_FIND);
2068 	if (s != NULL)
2069 		return (s->s_alias);
2070 	return (NULL);
2071 }
2072 
2073 
2074 /*
2075 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
2076 */
2077 
2078 void
2079 stab_map_store(map, lhs, rhs)
2080 	register MAP *map;
2081 	char *lhs;
2082 	char *rhs;
2083 {
2084 	register STAB *s;
2085 
2086 	s = stab(lhs, ST_ALIAS, ST_ENTER);
2087 	s->s_alias = newstr(rhs);
2088 }
2089 
2090 
2091 /*
2092 **  STAB_MAP_OPEN -- initialize (reads data file)
2093 **
2094 **	This is a wierd case -- it is only intended as a fallback for
2095 **	aliases.  For this reason, opens for write (only during a
2096 **	"newaliases") always fails, and opens for read open the
2097 **	actual underlying text file instead of the database.
2098 */
2099 
2100 bool
2101 stab_map_open(map, mode)
2102 	register MAP *map;
2103 	int mode;
2104 {
2105 	FILE *af;
2106 	struct stat st;
2107 
2108 	if (tTd(38, 2))
2109 		printf("stab_map_open(%s, %s)\n",
2110 			map->map_mname, map->map_file);
2111 
2112 	if (mode != O_RDONLY)
2113 	{
2114 		errno = ENODEV;
2115 		return FALSE;
2116 	}
2117 
2118 	af = fopen(map->map_file, "r");
2119 	if (af == NULL)
2120 		return FALSE;
2121 	readaliases(map, af, FALSE, FALSE);
2122 
2123 	if (fstat(fileno(af), &st) >= 0)
2124 		map->map_mtime = st.st_mtime;
2125 	fclose(af);
2126 
2127 	return TRUE;
2128 }
2129 /*
2130 **  Implicit Modules
2131 **
2132 **	Tries several types.  For back compatibility of aliases.
2133 */
2134 
2135 
2136 /*
2137 **  IMPL_MAP_LOOKUP -- lookup in best open database
2138 */
2139 
2140 char *
2141 impl_map_lookup(map, name, av, pstat)
2142 	MAP *map;
2143 	char *name;
2144 	char **av;
2145 	int *pstat;
2146 {
2147 	if (tTd(38, 20))
2148 		printf("impl_map_lookup(%s, %s)\n",
2149 			map->map_mname, name);
2150 
2151 #ifdef NEWDB
2152 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2153 		return db_map_lookup(map, name, av, pstat);
2154 #endif
2155 #ifdef NDBM
2156 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2157 		return ndbm_map_lookup(map, name, av, pstat);
2158 #endif
2159 	return stab_map_lookup(map, name, av, pstat);
2160 }
2161 
2162 /*
2163 **  IMPL_MAP_STORE -- store in open databases
2164 */
2165 
2166 void
2167 impl_map_store(map, lhs, rhs)
2168 	MAP *map;
2169 	char *lhs;
2170 	char *rhs;
2171 {
2172 #ifdef NEWDB
2173 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2174 		db_map_store(map, lhs, rhs);
2175 #endif
2176 #ifdef NDBM
2177 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2178 		ndbm_map_store(map, lhs, rhs);
2179 #endif
2180 	stab_map_store(map, lhs, rhs);
2181 }
2182 
2183 /*
2184 **  IMPL_MAP_OPEN -- implicit database open
2185 */
2186 
2187 bool
2188 impl_map_open(map, mode)
2189 	MAP *map;
2190 	int mode;
2191 {
2192 	struct stat stb;
2193 
2194 	if (tTd(38, 2))
2195 		printf("impl_map_open(%s, %s, %d)\n",
2196 			map->map_mname, map->map_file, mode);
2197 
2198 	if (stat(map->map_file, &stb) < 0)
2199 	{
2200 		/* no alias file at all */
2201 		if (tTd(38, 3))
2202 			printf("no map file\n");
2203 		return FALSE;
2204 	}
2205 
2206 #ifdef NEWDB
2207 	map->map_mflags |= MF_IMPL_HASH;
2208 	if (hash_map_open(map, mode))
2209 	{
2210 #if defined(NDBM) && defined(NIS)
2211 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
2212 #endif
2213 			return TRUE;
2214 	}
2215 	else
2216 		map->map_mflags &= ~MF_IMPL_HASH;
2217 #endif
2218 #ifdef NDBM
2219 	map->map_mflags |= MF_IMPL_NDBM;
2220 	if (ndbm_map_open(map, mode))
2221 	{
2222 		return TRUE;
2223 	}
2224 	else
2225 		map->map_mflags &= ~MF_IMPL_NDBM;
2226 #endif
2227 
2228 #if defined(NEWDB) || defined(NDBM)
2229 	if (Verbose)
2230 		message("WARNING: cannot open alias database %s", map->map_file);
2231 #else
2232 	if (mode != O_RDONLY)
2233 		usrerr("Cannot rebuild aliases: no database format defined");
2234 #endif
2235 
2236 	return stab_map_open(map, mode);
2237 }
2238 
2239 
2240 /*
2241 **  IMPL_MAP_CLOSE -- close any open database(s)
2242 */
2243 
2244 void
2245 impl_map_close(map)
2246 	MAP *map;
2247 {
2248 	if (tTd(38, 20))
2249 		printf("impl_map_close(%s, %s, %x)\n",
2250 			map->map_mname, map->map_file, map->map_mflags);
2251 #ifdef NEWDB
2252 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2253 	{
2254 		db_map_close(map);
2255 		map->map_mflags &= ~MF_IMPL_HASH;
2256 	}
2257 #endif
2258 
2259 #ifdef NDBM
2260 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2261 	{
2262 		ndbm_map_close(map);
2263 		map->map_mflags &= ~MF_IMPL_NDBM;
2264 	}
2265 #endif
2266 }
2267 /*
2268 **  User map class.
2269 **
2270 **	Provides access to the system password file.
2271 */
2272 
2273 /*
2274 **  USER_MAP_OPEN -- open user map
2275 **
2276 **	Really just binds field names to field numbers.
2277 */
2278 
2279 bool
2280 user_map_open(map, mode)
2281 	MAP *map;
2282 	int mode;
2283 {
2284 	if (tTd(38, 2))
2285 		printf("user_map_open(%s)\n", map->map_mname);
2286 
2287 	if (mode != O_RDONLY)
2288 	{
2289 		/* issue a pseudo-error message */
2290 #ifdef ENOSYS
2291 		errno = ENOSYS;
2292 #else
2293 # ifdef EFTYPE
2294 		errno = EFTYPE;
2295 # else
2296 		errno = ENXIO;
2297 # endif
2298 #endif
2299 		return FALSE;
2300 	}
2301 	if (map->map_valcolnm == NULL)
2302 		/* nothing */ ;
2303 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
2304 		map->map_valcolno = 1;
2305 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
2306 		map->map_valcolno = 2;
2307 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
2308 		map->map_valcolno = 3;
2309 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
2310 		map->map_valcolno = 4;
2311 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
2312 		map->map_valcolno = 5;
2313 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2314 		map->map_valcolno = 6;
2315 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2316 		map->map_valcolno = 7;
2317 	else
2318 	{
2319 		syserr("User map %s: unknown column name %s",
2320 			map->map_mname, map->map_valcolnm);
2321 		return FALSE;
2322 	}
2323 	return TRUE;
2324 }
2325 
2326 
2327 /*
2328 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2329 */
2330 
2331 char *
2332 user_map_lookup(map, key, av, statp)
2333 	MAP *map;
2334 	char *key;
2335 	char **av;
2336 	int *statp;
2337 {
2338 	struct passwd *pw;
2339 
2340 	if (tTd(38, 20))
2341 		printf("user_map_lookup(%s, %s)\n",
2342 			map->map_mname, key);
2343 
2344 	pw = sm_getpwnam(key);
2345 	if (pw == NULL)
2346 		return NULL;
2347 	if (bitset(MF_MATCHONLY, map->map_mflags))
2348 		return map_rewrite(map, key, strlen(key), NULL);
2349 	else
2350 	{
2351 		char *rwval = NULL;
2352 		char buf[30];
2353 
2354 		switch (map->map_valcolno)
2355 		{
2356 		  case 0:
2357 		  case 1:
2358 			rwval = pw->pw_name;
2359 			break;
2360 
2361 		  case 2:
2362 			rwval = pw->pw_passwd;
2363 			break;
2364 
2365 		  case 3:
2366 			sprintf(buf, "%d", pw->pw_uid);
2367 			rwval = buf;
2368 			break;
2369 
2370 		  case 4:
2371 			sprintf(buf, "%d", pw->pw_gid);
2372 			rwval = buf;
2373 			break;
2374 
2375 		  case 5:
2376 			rwval = pw->pw_gecos;
2377 			break;
2378 
2379 		  case 6:
2380 			rwval = pw->pw_dir;
2381 			break;
2382 
2383 		  case 7:
2384 			rwval = pw->pw_shell;
2385 			break;
2386 		}
2387 		return map_rewrite(map, rwval, strlen(rwval), av);
2388 	}
2389 }
2390 /*
2391 **  BESTMX -- find the best MX for a name
2392 **
2393 **	This is really a hack, but I don't see any obvious way
2394 **	to generalize it at the moment.
2395 */
2396 
2397 #if NAMED_BIND
2398 
2399 char *
2400 bestmx_map_lookup(map, name, av, statp)
2401 	MAP *map;
2402 	char *name;
2403 	char **av;
2404 	int *statp;
2405 {
2406         int nmx;
2407         auto int rcode;
2408         char *mxhosts[MAXMXHOSTS + 1];
2409 
2410 	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
2411 	if (nmx <= 0)
2412 		return NULL;
2413 	if (bitset(MF_MATCHONLY, map->map_mflags))
2414 		return map_rewrite(map, name, strlen(name), NULL);
2415 	else
2416 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
2417 }
2418 
2419 #endif
2420 /*
2421 **  Sequenced map type.
2422 **
2423 **	Tries each map in order until something matches, much like
2424 **	implicit.  Stores go to the first map in the list that can
2425 **	support storing.
2426 **
2427 **	This is slightly unusual in that there are two interfaces.
2428 **	The "sequence" interface lets you stack maps arbitrarily.
2429 **	The "switch" interface builds a sequence map by looking
2430 **	at a system-dependent configuration file such as
2431 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2432 **
2433 **	We don't need an explicit open, since all maps are
2434 **	opened during startup, including underlying maps.
2435 */
2436 
2437 /*
2438 **  SEQ_MAP_PARSE -- Sequenced map parsing
2439 */
2440 
2441 bool
2442 seq_map_parse(map, ap)
2443 	MAP *map;
2444 	char *ap;
2445 {
2446 	int maxmap;
2447 
2448 	if (tTd(38, 2))
2449 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2450 	maxmap = 0;
2451 	while (*ap != '\0')
2452 	{
2453 		register char *p;
2454 		STAB *s;
2455 
2456 		/* find beginning of map name */
2457 		while (isascii(*ap) && isspace(*ap))
2458 			ap++;
2459 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2460 			continue;
2461 		if (*p != '\0')
2462 			*p++ = '\0';
2463 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2464 			p++;
2465 		if (*ap == '\0')
2466 		{
2467 			ap = p;
2468 			continue;
2469 		}
2470 		s = stab(ap, ST_MAP, ST_FIND);
2471 		if (s == NULL)
2472 		{
2473 			syserr("Sequence map %s: unknown member map %s",
2474 				map->map_mname, ap);
2475 		}
2476 		else if (maxmap == MAXMAPSTACK)
2477 		{
2478 			syserr("Sequence map %s: too many member maps (%d max)",
2479 				map->map_mname, MAXMAPSTACK);
2480 			maxmap++;
2481 		}
2482 		else if (maxmap < MAXMAPSTACK)
2483 		{
2484 			map->map_stack[maxmap++] = &s->s_map;
2485 		}
2486 		ap = p;
2487 	}
2488 	return TRUE;
2489 }
2490 
2491 
2492 /*
2493 **  SWITCH_MAP_OPEN -- open a switched map
2494 **
2495 **	This looks at the system-dependent configuration and builds
2496 **	a sequence map that does the same thing.
2497 **
2498 **	Every system must define a switch_map_find routine in conf.c
2499 **	that will return the list of service types associated with a
2500 **	given service class.
2501 */
2502 
2503 bool
2504 switch_map_open(map, mode)
2505 	MAP *map;
2506 	int mode;
2507 {
2508 	int mapno;
2509 	int nmaps;
2510 	char *maptype[MAXMAPSTACK];
2511 
2512 	if (tTd(38, 2))
2513 		printf("switch_map_open(%s, %s, %d)\n",
2514 			map->map_mname, map->map_file, mode);
2515 
2516 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2517 	if (tTd(38, 19))
2518 	{
2519 		printf("\tswitch_map_find => %d\n", nmaps);
2520 		for (mapno = 0; mapno < nmaps; mapno++)
2521 			printf("\t\t%s\n", maptype[mapno]);
2522 	}
2523 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2524 		return FALSE;
2525 
2526 	for (mapno = 0; mapno < nmaps; mapno++)
2527 	{
2528 		register STAB *s;
2529 		char nbuf[MAXNAME + 1];
2530 
2531 		if (maptype[mapno] == NULL)
2532 			continue;
2533 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2534 		s = stab(nbuf, ST_MAP, ST_FIND);
2535 		if (s == NULL)
2536 		{
2537 			syserr("Switch map %s: unknown member map %s",
2538 				map->map_mname, nbuf);
2539 		}
2540 		else
2541 		{
2542 			map->map_stack[mapno] = &s->s_map;
2543 			if (tTd(38, 4))
2544 				printf("\tmap_stack[%d] = %s:%s\n",
2545 					mapno, s->s_map.map_class->map_cname,
2546 					nbuf);
2547 		}
2548 	}
2549 	return TRUE;
2550 }
2551 
2552 
2553 /*
2554 **  SEQ_MAP_CLOSE -- close all underlying maps
2555 */
2556 
2557 seq_map_close(map)
2558 	MAP *map;
2559 {
2560 	int mapno;
2561 
2562 	if (tTd(38, 20))
2563 		printf("seq_map_close(%s)\n", map->map_mname);
2564 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2565 	{
2566 		MAP *mm = map->map_stack[mapno];
2567 
2568 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2569 			continue;
2570 		mm->map_class->map_close(mm);
2571 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
2572 	}
2573 }
2574 
2575 
2576 /*
2577 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2578 */
2579 
2580 char *
2581 seq_map_lookup(map, key, args, pstat)
2582 	MAP *map;
2583 	char *key;
2584 	char **args;
2585 	int *pstat;
2586 {
2587 	int mapno;
2588 	int mapbit = 0x01;
2589 
2590 	if (tTd(38, 20))
2591 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2592 
2593 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2594 	{
2595 		MAP *mm = map->map_stack[mapno];
2596 		int stat = 0;
2597 		char *rv;
2598 
2599 		if (mm == NULL)
2600 			continue;
2601 		if (!bitset(MF_OPEN, mm->map_mflags))
2602 		{
2603 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2604 			{
2605 				*pstat = EX_UNAVAILABLE;
2606 				return NULL;
2607 			}
2608 			continue;
2609 		}
2610 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2611 		if (rv != NULL)
2612 			return rv;
2613 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2614 			return NULL;
2615 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2616 		{
2617 			*pstat = stat;
2618 			return NULL;
2619 		}
2620 	}
2621 	return NULL;
2622 }
2623 
2624 
2625 /*
2626 **  SEQ_MAP_STORE -- sequenced map store
2627 */
2628 
2629 void
2630 seq_map_store(map, key, val)
2631 	MAP *map;
2632 	char *key;
2633 	char *val;
2634 {
2635 	int mapno;
2636 
2637 	if (tTd(38, 12))
2638 		printf("seq_map_store(%s, %s, %s)\n",
2639 			map->map_mname, key, val);
2640 
2641 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2642 	{
2643 		MAP *mm = map->map_stack[mapno];
2644 
2645 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2646 			continue;
2647 
2648 		mm->map_class->map_store(mm, key, val);
2649 		return;
2650 	}
2651 	syserr("seq_map_store(%s, %s, %s): no writable map",
2652 		map->map_mname, key, val);
2653 }
2654 /*
2655 **  GETCANONNAME -- look up name using service switch
2656 **
2657 **	Parameters:
2658 **		host -- the host name to look up.
2659 **		hbsize -- the size of the host buffer.
2660 **		trymx -- if set, try MX records.
2661 **
2662 **	Returns:
2663 **		TRUE -- if the host was found.
2664 **		FALSE -- otherwise.
2665 */
2666 
2667 bool
2668 getcanonname(host, hbsize, trymx)
2669 	char *host;
2670 	int hbsize;
2671 	bool trymx;
2672 {
2673 	int nmaps;
2674 	int mapno;
2675 	bool found = FALSE;
2676 	auto int stat;
2677 	char *maptype[MAXMAPSTACK];
2678 	short mapreturn[MAXMAPACTIONS];
2679 	extern int h_errno;
2680 
2681 	nmaps = switch_map_find("hosts", maptype, mapreturn);
2682 	for (mapno = 0; mapno < nmaps; mapno++)
2683 	{
2684 		int i;
2685 
2686 		if (tTd(38, 20))
2687 			printf("getcanonname(%s), trying %s\n",
2688 				host, maptype[mapno]);
2689 		if (strcmp("files", maptype[mapno]) == 0)
2690 			found = text_getcanonname(host, hbsize, &stat);
2691 #ifdef NIS
2692 		else if (strcmp("nis", maptype[mapno]) == 0)
2693 			found = nis_getcanonname(host, hbsize, &stat);
2694 #endif
2695 #ifdef NISPLUS
2696 		else if (strcmp("nisplus", maptype[mapno]) == 0)
2697 			found = nisplus_getcanonname(host, hbsize, &stat);
2698 #endif
2699 #if NAMED_BIND
2700 		else if (strcmp("dns", maptype[mapno]) == 0)
2701 			found = dns_getcanonname(host, hbsize, trymx, &stat);
2702 #endif
2703 		else
2704 		{
2705 			found = FALSE;
2706 			stat = EX_UNAVAILABLE;
2707 		}
2708 		if (found)
2709 			break;
2710 
2711 		/* see if we should continue */
2712 		if (stat == EX_TEMPFAIL)
2713 			i = MA_TRYAGAIN;
2714 		else if (stat == EX_NOHOST)
2715 			i = MA_NOTFOUND;
2716 		else
2717 			i = MA_UNAVAIL;
2718 		if (bitset(1 << mapno, mapreturn[i]))
2719 			break;
2720 	}
2721 
2722 	if (found)
2723 	{
2724 		char *d;
2725 
2726 		if (tTd(38, 20))
2727 			printf("getcanonname(%s), found\n", host);
2728 
2729 		/*
2730 		**  If returned name is still single token, compensate
2731 		**  by tagging on $m.  This is because some sites set
2732 		**  up their DNS or NIS databases wrong.
2733 		*/
2734 
2735 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
2736 		{
2737 			d = macvalue('m', CurEnv);
2738 			if (d != NULL &&
2739 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
2740 			{
2741 				if (host[strlen(host) - 1] != '.')
2742 					strcat(host, ".");
2743 				strcat(host, d);
2744 			}
2745 			else
2746 			{
2747 				return FALSE;
2748 			}
2749 		}
2750 		return TRUE;
2751 	}
2752 
2753 	if (tTd(38, 20))
2754 		printf("getcanonname(%s), failed, stat=%d\n", host, stat);
2755 
2756 #if NAMED_BIND
2757 	if (stat == EX_NOHOST)
2758 		h_errno = HOST_NOT_FOUND;
2759 	else
2760 		h_errno = TRY_AGAIN;
2761 #endif
2762 
2763 	return FALSE;
2764 }
2765 /*
2766 **  NULL stubs
2767 */
2768 
2769 bool
2770 null_map_open(map, mode)
2771 	MAP *map;
2772 	int mode;
2773 {
2774 	return TRUE;
2775 }
2776 
2777 void
2778 null_map_close(map)
2779 	MAP *map;
2780 {
2781 	return;
2782 }
2783 
2784 void
2785 null_map_store(map, key, val)
2786 	MAP *map;
2787 	char *key;
2788 	char *val;
2789 {
2790 	return;
2791 }
2792 
2793 
2794 /*
2795 **  BOGUS stubs
2796 */
2797 
2798 char *
2799 bogus_map_lookup(map, key, args, pstat)
2800 	MAP *map;
2801 	char *key;
2802 	char **args;
2803 	int *pstat;
2804 {
2805 	*pstat = EX_TEMPFAIL;
2806 	return NULL;
2807 }
2808 
2809 MAPCLASS	BogusMapClass =
2810 {
2811 	"bogus-map",		NULL,		0,
2812 	NULL,		bogus_map_lookup,	null_map_store,
2813 	null_map_open,	null_map_close,
2814 };
2815