xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision f7b88020)
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.67 (Berkeley) 05/25/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;	/* forward reference 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 		if (strstr(map->map_file, "/yp/") != NULL)
654 		{
655 			(void) sprintf(buf, "%010ld", curtime());
656 			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
657 
658 			(void) gethostname(buf, sizeof buf);
659 			ndbm_map_store(map, "YP_MASTER_NAME", buf);
660 		}
661 
662 		if (inclnull)
663 			map->map_mflags |= MF_INCLNULL;
664 #endif
665 
666 		/* write out the distinguished alias */
667 		ndbm_map_store(map, "@", "@");
668 	}
669 	dbm_close((DBM *) map->map_db1);
670 }
671 
672 #endif
673 /*
674 **  NEWDB (Hash and BTree) Modules
675 */
676 
677 #ifdef NEWDB
678 
679 /*
680 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
681 **
682 **	These do rather bizarre locking.  If you can lock on open,
683 **	do that to avoid the condition of opening a database that
684 **	is being rebuilt.  If you don't, we'll try to fake it, but
685 **	there will be a race condition.  If opening for read-only,
686 **	we immediately release the lock to avoid freezing things up.
687 **	We really ought to hold the lock, but guarantee that we won't
688 **	be pokey about it.  That's hard to do.
689 */
690 
691 bool
692 bt_map_open(map, mode)
693 	MAP *map;
694 	int mode;
695 {
696 	DB *db;
697 	int i;
698 	int omode;
699 	int fd;
700 	struct stat st;
701 	char buf[MAXNAME + 1];
702 
703 	if (tTd(38, 2))
704 		printf("bt_map_open(%s, %s, %d)\n",
705 			map->map_mname, map->map_file, mode);
706 
707 	omode = mode;
708 	if (omode == O_RDWR)
709 	{
710 		omode |= O_CREAT|O_TRUNC;
711 #if defined(O_EXLOCK) && HASFLOCK
712 		omode |= O_EXLOCK;
713 # if !OLD_NEWDB
714 	}
715 	else
716 	{
717 		omode |= O_SHLOCK;
718 # endif
719 #endif
720 	}
721 
722 	(void) strcpy(buf, map->map_file);
723 	i = strlen(buf);
724 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
725 		(void) strcat(buf, ".db");
726 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
727 	if (db == NULL)
728 	{
729 #ifdef MAYBENEXTRELEASE
730 		if (aliaswait(map, ".db", FALSE))
731 			return TRUE;
732 #endif
733 		if (!bitset(MF_OPTIONAL, map->map_mflags))
734 			syserr("Cannot open BTREE database %s", map->map_file);
735 		return FALSE;
736 	}
737 #if !OLD_NEWDB
738 	fd = db->fd(db);
739 # if defined(O_EXLOCK) && HASFLOCK
740 	if (fd >= 0)
741 	{
742 		if (mode == O_RDONLY)
743 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
744 		else
745 			map->map_mflags |= MF_LOCKED;
746 	}
747 # else
748 	if (mode == O_RDWR && fd >= 0)
749 	{
750 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
751 			map->map_mflags |= MF_LOCKED;
752 	}
753 # endif
754 #endif
755 
756 	/* try to make sure that at least the database header is on disk */
757 	if (mode == O_RDWR)
758 #if OLD_NEWDB
759 		(void) db->sync(db);
760 #else
761 		(void) db->sync(db, 0);
762 
763 	if (fd >= 0 && fstat(fd, &st) >= 0)
764 		map->map_mtime = st.st_mtime;
765 #endif
766 
767 	map->map_db2 = (void *) db;
768 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
769 		if (!aliaswait(map, ".db", TRUE))
770 			return FALSE;
771 	return TRUE;
772 }
773 
774 
775 /*
776 **  HASH_MAP_INIT -- HASH-style map initialization
777 */
778 
779 bool
780 hash_map_open(map, mode)
781 	MAP *map;
782 	int mode;
783 {
784 	DB *db;
785 	int i;
786 	int omode;
787 	int fd;
788 	struct stat st;
789 	char buf[MAXNAME + 1];
790 
791 	if (tTd(38, 2))
792 		printf("hash_map_open(%s, %s, %d)\n",
793 			map->map_mname, map->map_file, mode);
794 
795 	omode = mode;
796 	if (omode == O_RDWR)
797 	{
798 		omode |= O_CREAT|O_TRUNC;
799 #if defined(O_EXLOCK) && HASFLOCK
800 		omode |= O_EXLOCK;
801 # if !OLD_NEWDB
802 	}
803 	else
804 	{
805 		omode |= O_SHLOCK;
806 # endif
807 #endif
808 	}
809 
810 	(void) strcpy(buf, map->map_file);
811 	i = strlen(buf);
812 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
813 		(void) strcat(buf, ".db");
814 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
815 	if (db == NULL)
816 	{
817 #ifdef MAYBENEXTRELEASE
818 		if (aliaswait(map, ".db", FALSE))
819 			return TRUE;
820 #endif
821 		if (!bitset(MF_OPTIONAL, map->map_mflags))
822 			syserr("Cannot open HASH database %s", map->map_file);
823 		return FALSE;
824 	}
825 #if !OLD_NEWDB
826 	fd = db->fd(db);
827 # if defined(O_EXLOCK) && HASFLOCK
828 	if (fd >= 0)
829 	{
830 		if (mode == O_RDONLY)
831 			(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
832 		else
833 			map->map_mflags |= MF_LOCKED;
834 	}
835 # else
836 	if (mode == O_RDWR && fd >= 0)
837 	{
838 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
839 			map->map_mflags |= MF_LOCKED;
840 	}
841 # endif
842 #endif
843 
844 	/* try to make sure that at least the database header is on disk */
845 	if (mode == O_RDWR)
846 #if OLD_NEWDB
847 		(void) db->sync(db);
848 #else
849 		(void) db->sync(db, 0);
850 
851 	if (fd >= 0 && fstat(fd, &st) >= 0)
852 		map->map_mtime = st.st_mtime;
853 #endif
854 
855 	map->map_db2 = (void *) db;
856 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
857 		if (!aliaswait(map, ".db", TRUE))
858 			return FALSE;
859 	return TRUE;
860 }
861 
862 
863 /*
864 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
865 */
866 
867 char *
868 db_map_lookup(map, name, av, statp)
869 	MAP *map;
870 	char *name;
871 	char **av;
872 	int *statp;
873 {
874 	DBT key, val;
875 	register DB *db = (DB *) map->map_db2;
876 	int st;
877 	int saveerrno;
878 	int fd;
879 	char keybuf[MAXNAME + 1];
880 
881 	if (tTd(38, 20))
882 		printf("db_map_lookup(%s, %s)\n",
883 			map->map_mname, name);
884 
885 	key.size = strlen(name);
886 	if (key.size > sizeof keybuf - 1)
887 		key.size = sizeof keybuf - 1;
888 	key.data = keybuf;
889 	bcopy(name, keybuf, key.size + 1);
890 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
891 		makelower(keybuf);
892 #if !OLD_NEWDB
893 	fd = db->fd(db);
894 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
895 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
896 #endif
897 	st = 1;
898 	if (bitset(MF_TRY0NULL, map->map_mflags))
899 	{
900 		st = db->get(db, &key, &val, 0);
901 		if (st == 0)
902 			map->map_mflags &= ~MF_TRY1NULL;
903 	}
904 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
905 	{
906 		key.size++;
907 		st = db->get(db, &key, &val, 0);
908 		if (st == 0)
909 			map->map_mflags &= ~MF_TRY0NULL;
910 	}
911 	saveerrno = errno;
912 #if !OLD_NEWDB
913 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
914 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
915 #endif
916 	if (st != 0)
917 	{
918 		errno = saveerrno;
919 		if (st < 0)
920 			syserr("db_map_lookup: get (%s)", name);
921 		return NULL;
922 	}
923 	if (bitset(MF_MATCHONLY, map->map_mflags))
924 		return map_rewrite(map, name, strlen(name), NULL);
925 	else
926 		return map_rewrite(map, val.data, val.size, av);
927 }
928 
929 
930 /*
931 **  DB_MAP_STORE -- store a datum in the NEWDB database
932 */
933 
934 void
935 db_map_store(map, lhs, rhs)
936 	register MAP *map;
937 	char *lhs;
938 	char *rhs;
939 {
940 	int stat;
941 	DBT key;
942 	DBT data;
943 	register DB *db = map->map_db2;
944 
945 	if (tTd(38, 20))
946 		printf("db_map_store(%s, %s, %s)\n",
947 			map->map_mname, lhs, rhs);
948 
949 	key.size = strlen(lhs);
950 	key.data = lhs;
951 
952 	data.size = strlen(rhs);
953 	data.data = rhs;
954 
955 	if (bitset(MF_INCLNULL, map->map_mflags))
956 	{
957 		key.size++;
958 		data.size++;
959 	}
960 
961 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
962 	if (stat > 0)
963 	{
964 		if (!bitset(MF_APPEND, map->map_mflags))
965 			usrerr("050 Warning: duplicate alias name %s", lhs);
966 		else
967 		{
968 			static char *buf = NULL;
969 			static int bufsiz = 0;
970 			DBT old;
971 
972 			old.data = db_map_lookup(map, key.data, NULL, &stat);
973 			if (old.data != NULL)
974 			{
975 				old.size = strlen(old.data);
976 				if (data.size + old.size + 2 > bufsiz)
977 				{
978 					if (buf != NULL)
979 						(void) free(buf);
980 					bufsiz = data.size + old.size + 2;
981 					buf = xalloc(bufsiz);
982 				}
983 				sprintf(buf, "%s,%s", data.data, old.data);
984 				data.size = data.size + old.size + 1;
985 				data.data = buf;
986 				if (tTd(38, 9))
987 					printf("db_map_store append=%s\n", data.data);
988 			}
989 		}
990 		stat = db->put(db, &key, &data, 0);
991 	}
992 	if (stat != 0)
993 		syserr("readaliases: db put (%s)", lhs);
994 }
995 
996 
997 /*
998 **  DB_MAP_CLOSE -- add distinguished entries and close the database
999 */
1000 
1001 void
1002 db_map_close(map)
1003 	MAP *map;
1004 {
1005 	register DB *db = map->map_db2;
1006 
1007 	if (tTd(38, 9))
1008 		printf("db_map_close(%s, %s, %x)\n",
1009 			map->map_mname, map->map_file, map->map_mflags);
1010 
1011 	if (bitset(MF_WRITABLE, map->map_mflags))
1012 	{
1013 		/* write out the distinguished alias */
1014 		db_map_store(map, "@", "@");
1015 	}
1016 
1017 	if (db->close(db) != 0)
1018 		syserr("readaliases: db close failure");
1019 }
1020 
1021 #endif
1022 /*
1023 **  NIS Modules
1024 */
1025 
1026 # ifdef NIS
1027 
1028 # ifndef YPERR_BUSY
1029 #  define YPERR_BUSY	16
1030 # endif
1031 
1032 /*
1033 **  NIS_MAP_OPEN -- open DBM map
1034 */
1035 
1036 bool
1037 nis_map_open(map, mode)
1038 	MAP *map;
1039 	int mode;
1040 {
1041 	int yperr;
1042 	register char *p;
1043 	auto char *vp;
1044 	auto int vsize;
1045 	char *master;
1046 
1047 	if (tTd(38, 2))
1048 		printf("nis_map_open(%s, %s)\n",
1049 			map->map_mname, map->map_file);
1050 
1051 	if (mode != O_RDONLY)
1052 	{
1053 		/* issue a pseudo-error message */
1054 #ifdef ENOSYS
1055 		errno = ENOSYS;
1056 #else
1057 # ifdef EFTYPE
1058 		errno = EFTYPE;
1059 # else
1060 		errno = ENXIO;
1061 # endif
1062 #endif
1063 		return FALSE;
1064 	}
1065 
1066 	p = strchr(map->map_file, '@');
1067 	if (p != NULL)
1068 	{
1069 		*p++ = '\0';
1070 		if (*p != '\0')
1071 			map->map_domain = p;
1072 	}
1073 
1074 	if (*map->map_file == '\0')
1075 		map->map_file = "mail.aliases";
1076 
1077 	if (map->map_domain == NULL)
1078 	{
1079 		yperr = yp_get_default_domain(&map->map_domain);
1080 		if (yperr != 0)
1081 		{
1082 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1083 				syserr("421 NIS map %s specified, but NIS not running\n",
1084 					map->map_file);
1085 			return FALSE;
1086 		}
1087 	}
1088 
1089 	/* check to see if this map actually exists */
1090 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1091 			&vp, &vsize);
1092 	if (tTd(38, 10))
1093 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1094 			map->map_domain, map->map_file, yperr_string(yperr));
1095 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1096 	{
1097 		if (!bitset(MF_ALIAS, map->map_mflags) ||
1098 		    aliaswait(map, NULL, TRUE))
1099 			return TRUE;
1100 	}
1101 
1102 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1103 	{
1104 		syserr("421 Cannot bind to map %s in domain %s: %s",
1105 			map->map_file, map->map_domain, yperr_string(yperr));
1106 	}
1107 
1108 	return FALSE;
1109 }
1110 
1111 
1112 /*
1113 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1114 */
1115 
1116 char *
1117 nis_map_lookup(map, name, av, statp)
1118 	MAP *map;
1119 	char *name;
1120 	char **av;
1121 	int *statp;
1122 {
1123 	char *vp;
1124 	auto int vsize;
1125 	int buflen;
1126 	int yperr;
1127 	char keybuf[MAXNAME + 1];
1128 
1129 	if (tTd(38, 20))
1130 		printf("nis_map_lookup(%s, %s)\n",
1131 			map->map_mname, name);
1132 
1133 	buflen = strlen(name);
1134 	if (buflen > sizeof keybuf - 1)
1135 		buflen = sizeof keybuf - 1;
1136 	bcopy(name, keybuf, buflen + 1);
1137 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1138 		makelower(keybuf);
1139 	yperr = YPERR_KEY;
1140 	if (bitset(MF_TRY0NULL, map->map_mflags))
1141 	{
1142 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1143 			     &vp, &vsize);
1144 		if (yperr == 0)
1145 			map->map_mflags &= ~MF_TRY1NULL;
1146 	}
1147 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1148 	{
1149 		buflen++;
1150 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1151 			     &vp, &vsize);
1152 		if (yperr == 0)
1153 			map->map_mflags &= ~MF_TRY0NULL;
1154 	}
1155 	if (yperr != 0)
1156 	{
1157 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1158 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1159 		return NULL;
1160 	}
1161 	if (bitset(MF_MATCHONLY, map->map_mflags))
1162 		return map_rewrite(map, name, strlen(name), NULL);
1163 	else
1164 		return map_rewrite(map, vp, vsize, av);
1165 }
1166 
1167 
1168 /*
1169 **  NIS_GETCANONNAME -- look up canonical name in NIS
1170 */
1171 
1172 bool
1173 nis_getcanonname(name, hbsize, statp)
1174 	char *name;
1175 	int hbsize;
1176 	int *statp;
1177 {
1178 	char *vp;
1179 	auto int vsize;
1180 	int keylen;
1181 	int yperr;
1182 	static bool try0null = TRUE;
1183 	static bool try1null = TRUE;
1184 	static char *yp_domain = NULL;
1185 	char *domain, *p;
1186 	char host_record[MAXLINE];
1187 	char buf[MAXNAME];
1188 	char *cname;
1189 	extern char *get_column();
1190 
1191 	if (tTd(38, 20))
1192 		printf("nis_getcanonname(%s)\n", name);
1193 
1194 	shorten_hostname(name);
1195 
1196 	/* we only accept single token search key */
1197 	if (strchr(name, '.'))
1198 	{
1199 		*statp = EX_NOHOST;
1200 		return FALSE;
1201 	}
1202 
1203 	keylen = strlen(name);
1204 
1205 	if (yp_domain == NULL)
1206 		yp_get_default_domain(&yp_domain);
1207 	makelower(name);
1208 	yperr = YPERR_KEY;
1209 	if (try0null)
1210 	{
1211 		yperr = yp_match(yp_domain, "hosts.byname", name, keylen,
1212 			     &vp, &vsize);
1213 		if (yperr == 0)
1214 			try1null = FALSE;
1215 	}
1216 	if (yperr == YPERR_KEY && try1null)
1217 	{
1218 		keylen++;
1219 		yperr = yp_match(yp_domain, "hosts.byname", name, keylen,
1220 			     &vp, &vsize);
1221 		if (yperr == 0)
1222 			try0null = FALSE;
1223 	}
1224 	if (yperr != 0)
1225 	{
1226 		if (yperr == YPERR_KEY)
1227 			*statp = EX_NOHOST;
1228 		else if (yperr == YPERR_BUSY)
1229 			*statp = EX_TEMPFAIL;
1230 		else
1231 			*statp = EX_UNAVAILABLE;
1232 		return FALSE;
1233 	}
1234 	strncpy(host_record, vp, vsize);
1235 	host_record[vsize] = '\0';
1236 	if (tTd(38, 44))
1237 		printf("got record `%s'\n", host_record);
1238 	cname = get_column(host_record, 1, '\0', buf);
1239 	if (cname == NULL)
1240 	{
1241 		/* this should not happen, but.... */
1242 		*statp = EX_NOHOST;
1243 		return FALSE;
1244 	}
1245 
1246 	if (hbsize >= strlen(cname))
1247 	{
1248 		strcpy(name, cname);
1249 		*statp = EX_OK;
1250 		return TRUE;
1251 	}
1252 	*statp = EX_UNAVAILABLE;
1253 	return FALSE;
1254 }
1255 
1256 #endif
1257 /*
1258 **  NISPLUS Modules
1259 **
1260 **	This code donated by Sun Microsystems.
1261 */
1262 
1263 #ifdef NISPLUS
1264 
1265 #undef NIS /* symbol conflict in nis.h */
1266 #include <rpcsvc/nis.h>
1267 #include <rpcsvc/nislib.h>
1268 
1269 #define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1270 #define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1271 #define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1272 #define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
1273 
1274 /*
1275 **  NISPLUS_MAP_OPEN -- open nisplus table
1276 */
1277 
1278 bool
1279 nisplus_map_open(map, mode)
1280 	MAP *map;
1281 	int mode;
1282 {
1283 	register char *p;
1284 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1285 	nis_result *res = NULL;
1286 	u_int objs_len;
1287 	nis_object *obj_ptr;
1288 	int retry_cnt, max_col, i;
1289 
1290 	if (tTd(38, 2))
1291 		printf("nisplus_map_open(%s, %s, %d)\n",
1292 			map->map_mname, map->map_file, mode);
1293 
1294 	if (mode != O_RDONLY)
1295 	{
1296 		errno = ENODEV;
1297 		return FALSE;
1298 	}
1299 
1300 	if (*map->map_file == '\0')
1301 		map->map_file = "mail_aliases.org_dir";
1302 
1303 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1304 	{
1305 		/* set default NISPLUS Domain to $m */
1306 		extern char *nisplus_default_domain();
1307 
1308 		map->map_domain = newstr(nisplus_default_domain());
1309 		if (tTd(38, 2))
1310 			printf("nisplus_map_open(%s): using domain %s\n",
1311 				 map->map_file, map->map_domain);
1312 	}
1313 	if (!PARTIAL_NAME(map->map_file))
1314 		map->map_domain = newstr("");
1315 
1316 	/* check to see if this map actually exists */
1317 	if (PARTIAL_NAME(map->map_file))
1318 		sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
1319 	else
1320 		strcpy(qbuf, map->map_file);
1321 
1322 	retry_cnt = 0;
1323 	while (res == NULL || res->status != NIS_SUCCESS)
1324 	{
1325 		res = nis_lookup(qbuf, FOLLOW_LINKS);
1326 		switch (res->status)
1327 		{
1328 		  case NIS_SUCCESS:
1329 		  case NIS_TRYAGAIN:
1330 		  case NIS_RPCERROR:
1331 		  case NIS_NAMEUNREACHABLE:
1332 			break;
1333 
1334 		  default:		/* all other nisplus errors */
1335 #if 0
1336 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1337 				syserr("421 Cannot find table %s.%s: %s",
1338 					map->map_file, map->map_domain,
1339 					nis_sperrno(res->status));
1340 #endif
1341 			errno = EBADR;
1342 			return FALSE;
1343 		}
1344 		sleep(2);		/* try not to overwhelm hosed server */
1345 		if (retry_cnt++ > 4)
1346 		{
1347 			errno = EBADR;
1348 			return FALSE;
1349 		}
1350 	}
1351 
1352 	if (NIS_RES_NUMOBJ(res) != 1 ||
1353 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1354 	{
1355 		if (tTd(38, 10))
1356 			printf("nisplus_map_open: %s is not a table\n", qbuf);
1357 #if 0
1358 		if (!bitset(MF_OPTIONAL, map->map_mflags))
1359 			syserr("421 %s.%s: %s is not a table",
1360 				map->map_file, map->map_domain,
1361 				nis_sperrno(res->status));
1362 #endif
1363 		errno = EBADR;
1364 		return FALSE;
1365 	}
1366 	/* default key column is column 0 */
1367 	if (map->map_keycolnm == NULL)
1368 		map->map_keycolnm = newstr(COL_NAME(res,0));
1369 
1370 	max_col = COL_MAX(res);
1371 
1372 	/* verify the key column exist */
1373 	for (i=0; i< max_col; i++)
1374 	{
1375 		if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
1376 			break;
1377 	}
1378 	if (i == max_col)
1379 	{
1380 		if (tTd(38, 2))
1381 			printf("nisplus_map_open(%s): can not find key column %s\n",
1382 				map->map_file, map->map_keycolnm);
1383 		errno = EBADR;
1384 		return FALSE;
1385 	}
1386 
1387 	/* default value column is the last column */
1388 	if (map->map_valcolnm == NULL)
1389 	{
1390 		map->map_valcolno = max_col - 1;
1391 		return TRUE;
1392 	}
1393 
1394 	for (i=0; i< max_col; i++)
1395 	{
1396 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1397 		{
1398 			map->map_valcolno = i;
1399 			return TRUE;
1400 		}
1401 	}
1402 
1403 	if (tTd(38, 2))
1404 		printf("nisplus_map_open(%s): can not find column %s\n",
1405 			 map->map_file, map->map_keycolnm);
1406 	errno = EBADR;
1407 	return FALSE;
1408 }
1409 
1410 
1411 /*
1412 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1413 */
1414 
1415 char *
1416 nisplus_map_lookup(map, name, av, statp)
1417 	MAP *map;
1418 	char *name;
1419 	char **av;
1420 	int *statp;
1421 {
1422 	char *vp;
1423 	auto int vsize;
1424 	int buflen;
1425 	char search_key[MAXNAME + 1];
1426 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
1427 	nis_result *result;
1428 
1429 	if (tTd(38, 20))
1430 		printf("nisplus_map_lookup(%s, %s)\n",
1431 			map->map_mname, name);
1432 
1433 	if (!bitset(MF_OPEN, map->map_mflags))
1434 	{
1435 		if (nisplus_map_open(map, O_RDONLY))
1436 			map->map_mflags |= MF_OPEN;
1437 		else
1438 		{
1439 			*statp = EX_UNAVAILABLE;
1440 			return NULL;
1441 		}
1442 	}
1443 
1444 	buflen = strlen(name);
1445 	if (buflen > sizeof search_key - 1)
1446 		buflen = sizeof search_key - 1;
1447 	bcopy(name, search_key, buflen + 1);
1448 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1449 		makelower(search_key);
1450 
1451 	/* construct the query */
1452 	if (PARTIAL_NAME(map->map_file))
1453 		sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
1454 			search_key, map->map_file, map->map_domain);
1455 	else
1456 		sprintf(qbuf, "[%s=%s],%s", map->map_keycolnm,
1457 			search_key, map->map_file);
1458 
1459 	if (tTd(38, 20))
1460 		printf("qbuf=%s\n", qbuf);
1461 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1462 	if (result->status == NIS_SUCCESS)
1463 	{
1464 		int count;
1465 		char *str;
1466 
1467 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1468 		{
1469 			if (LogLevel > 10)
1470 				syslog(LOG_WARNING,
1471 				  "%s:Lookup error, expected 1 entry, got (%d)",
1472 				    map->map_file, count);
1473 
1474 			/* ignore second entry */
1475 			if (tTd(38, 20))
1476 				printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1477 					name, count);
1478 		}
1479 
1480 		vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1481 		/* set the length of the result */
1482 		if (vp == NULL)
1483 			vp = "";
1484 		vsize = strlen(vp);
1485 		if (tTd(38, 20))
1486 			printf("nisplus_map_lookup(%s), found %s\n",
1487 				name, vp);
1488 		if (bitset(MF_MATCHONLY, map->map_mflags))
1489 			str = map_rewrite(map, name, strlen(name), NULL);
1490 		else
1491 			str = map_rewrite(map, vp, vsize, av);
1492 		nis_freeresult(result);
1493 #ifdef MAP_EXIT_STAT
1494 		*statp = EX_OK;
1495 #endif
1496 		return str;
1497 	}
1498 	else
1499 	{
1500 #ifdef MAP_EXIT_STAT
1501 		if (result->status == NIS_NOTFOUND)
1502 			*statp = EX_NOTFOUND;
1503 		else if (result->status == NIS_TRYAGAIN)
1504 			*statp = EX_TEMPFAIL;
1505 		else
1506 		{
1507 			*statp = EX_UNAVAILABLE;
1508 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1509 		}
1510 #else
1511 		if ((result->status != NIS_NOTFOUND) &&
1512 		    (result->status != NIS_TRYAGAIN))
1513 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1514 #endif
1515 	}
1516 	if (tTd(38, 20))
1517 		printf("nisplus_map_lookup(%s), failed\n", name);
1518 	nis_freeresult(result);
1519 	return NULL;
1520 }
1521 
1522 
1523 
1524 /*
1525 **  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
1526 */
1527 
1528 bool
1529 nisplus_getcanonname(name, hbsize, statp)
1530 	char *name;
1531 	int hbsize;
1532 	int *statp;
1533 {
1534 	char *vp;
1535 	auto int vsize;
1536 	int buflen;
1537 	char buf[MAXLINE + NIS_MAXNAMELEN];
1538 	nis_result *result;
1539 	char *p;
1540 	int len;
1541 
1542 	shorten_hostname(name);
1543 
1544 	p = strchr(name, '.');
1545 	if (p == NULL)
1546 	{
1547 		/* single token */
1548 		sprintf(buf, "[name=%s],hosts.org_dir", name);
1549 	}
1550 	else if (p[1] != '\0')
1551 	{
1552 		/* multi token -- take only first token in name buf */
1553 		*p = '\0';
1554 		sprintf(buf, "[name=%s],hosts.org_dir.%s", name, &p[1]);
1555 	}
1556 	else
1557 	{
1558 		*statp = EX_NOHOST;
1559 		return FALSE;
1560 	}
1561 
1562 	if (tTd(38, 20))
1563 		printf("\nnisplus_getcanoname(%s), qbuf=%s\n",
1564 			 name, buf);
1565 
1566 	result = nis_list(buf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
1567 		NULL, NULL);
1568 
1569 	if (result->status == NIS_SUCCESS)
1570 	{
1571 		int count;
1572 		char *str;
1573 		char *domain;
1574 
1575 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
1576 		{
1577 #ifdef LOG
1578 			if (LogLevel > 10)
1579 				syslog(LOG_WARNING,
1580 				       "nisplus_getcanonname: Lookup error, expected 1 entry, got (%d)",
1581 				       count);
1582 #endif
1583 
1584 			/* ignore second entry */
1585 			if (tTd(38, 20))
1586 				printf("nisplus_getcanoname(%s), got %d entries, addtional entries ignores\n", name);
1587 		}
1588 
1589 		if (tTd(38, 20))
1590 			printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
1591 			       name, (NIS_RES_OBJECT(result))->zo_domain);
1592 
1593 
1594 		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
1595 		vsize = strlen(vp);
1596 		if (tTd(38, 20))
1597 			printf("nisplus_getcanonname(%s), found %s\n",
1598 				name, vp);
1599 		domain = macvalue('m', CurEnv);
1600 		if (domain == NULL)
1601 			domain = "";
1602 		if (hbsize > vsize + (int) strlen(domain) + 1)
1603 		{
1604 			if (domain[0] == '\0')
1605 				strcpy(name, vp);
1606 			else
1607 				sprintf(name, "%s.%s", vp, domain);
1608 			*statp = EX_OK;
1609 		}
1610 		else
1611 			*statp = EX_NOHOST;
1612 		nis_freeresult(result);
1613 		return TRUE;
1614 	}
1615 	else
1616 	{
1617 		if (result->status == NIS_NOTFOUND)
1618 			*statp = EX_NOHOST;
1619 		else if (result->status == NIS_TRYAGAIN)
1620 			*statp = EX_TEMPFAIL;
1621 		else
1622 			*statp = EX_UNAVAILABLE;
1623 	}
1624 	if (tTd(38, 20))
1625 		printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
1626 			name, result->status, *statp);
1627 	nis_freeresult(result);
1628 	return FALSE;
1629 }
1630 
1631 
1632 char *
1633 nisplus_default_domain()
1634 {
1635 	static char default_domain[MAXNAME + 1] = "";
1636 	char *p;
1637 
1638 	if (default_domain[0] != '\0')
1639 		return(default_domain);
1640 
1641 	p = nis_local_directory();
1642 	strcpy(default_domain, p);
1643 	return default_domain;
1644 }
1645 
1646 #endif /* NISPLUS */
1647 /*
1648 **  HESIOD Modules
1649 */
1650 
1651 #ifdef HESIOD
1652 
1653 #include <hesiod.h>
1654 
1655 char *
1656 hes_map_lookup(map, name, av, statp)
1657         MAP *map;
1658         char *name;
1659         char **av;
1660         int *statp;
1661 {
1662 	char **hp;
1663 	int i;
1664 
1665 	if (tTd(38, 20))
1666 		printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1667 
1668 	if (name[0] == '\\')
1669 	{
1670 		char *np;
1671 		int nl;
1672 		char nbuf[MAXNAME];
1673 
1674 		nl = strlen(name);
1675 		if (nl < sizeof nbuf - 1)
1676 			np = nbuf;
1677 		else
1678 			np = xalloc(strlen(name) + 2);
1679 		np[0] = '\\';
1680 		strcpy(&np[1], name);
1681 		hp = hes_resolve(np, map->map_file);
1682 		if (np != nbuf)
1683 			free(np);
1684 	}
1685 	else
1686 	{
1687 		hp = hes_resolve(name, map->map_file);
1688 	}
1689 	if (hp == NULL || hp[0] == NULL)
1690 		return NULL;
1691 
1692 	if (bitset(MF_MATCHONLY, map->map_mflags))
1693 		return map_rewrite(map, name, strlen(name), NULL);
1694 	else
1695 		return map_rewrite(map, hp[0], strlen(hp[0]), av);
1696 }
1697 
1698 #endif
1699 /*
1700 **  NeXT NETINFO Modules
1701 */
1702 
1703 #ifdef NETINFO
1704 
1705 #define NETINFO_DEFAULT_DIR		"/aliases"
1706 #define NETINFO_DEFAULT_PROPERTY	"members"
1707 
1708 
1709 /*
1710 **  NI_MAP_OPEN -- open NetInfo Aliases
1711 */
1712 
1713 bool
1714 ni_map_open(map, mode)
1715 	MAP *map;
1716 	int mode;
1717 {
1718 	char *p;
1719 
1720 	if (tTd(38, 20))
1721 		printf("ni_map_open: %s\n", map->map_file);
1722 
1723 	if (*map->map_file == '\0')
1724 		map->map_file = NETINFO_DEFAULT_DIR;
1725 
1726 	if (map->map_valcolnm == NULL)
1727 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1728 
1729 	if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
1730 		map->map_coldelim = ',';
1731 
1732 	return TRUE;
1733 }
1734 
1735 
1736 /*
1737 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
1738 */
1739 
1740 char *
1741 ni_map_lookup(map, name, av, statp)
1742 	MAP *map;
1743 	char *name;
1744 	char **av;
1745 	int *statp;
1746 {
1747 	char *res;
1748 	char *propval;
1749 	extern char *ni_propval();
1750 
1751 	if (tTd(38, 20))
1752 		printf("ni_map_lookup(%s, %s)\n",
1753 			map->map_mname, name);
1754 
1755 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
1756 			     map->map_valcolnm, map->map_coldelim);
1757 
1758 	if (propval == NULL)
1759 		return NULL;
1760 
1761 	if (bitset(MF_MATCHONLY, map->map_mflags))
1762 		res = map_rewrite(map, name, strlen(name), NULL);
1763 	else
1764 		res = map_rewrite(map, propval, strlen(propval), av);
1765 	free(propval);
1766 	return res;
1767 }
1768 
1769 #endif
1770 /*
1771 **  TEXT (unindexed text file) Modules
1772 **
1773 **	This code donated by Sun Microsystems.
1774 */
1775 
1776 
1777 /*
1778 **  TEXT_MAP_OPEN -- open text table
1779 */
1780 
1781 bool
1782 text_map_open(map, mode)
1783 	MAP *map;
1784 	int mode;
1785 {
1786 	struct stat sbuf;
1787 
1788 	if (tTd(38, 2))
1789 		printf("text_map_open(%s, %s, %d)\n",
1790 			map->map_mname, map->map_file, mode);
1791 
1792 	if (mode != O_RDONLY)
1793 	{
1794 		errno = ENODEV;
1795 		return FALSE;
1796 	}
1797 
1798 	if (*map->map_file == '\0')
1799 	{
1800 		if (tTd(38, 2))
1801 			printf("text_map_open: file name required\n");
1802 		return FALSE;
1803 	}
1804 
1805 	if (map->map_file[0] != '/')
1806 	{
1807 		if (tTd(38, 2))
1808 			printf("text_map_open(%s): file name must be fully qualified\n",
1809 				map->map_file);
1810 		return FALSE;
1811 	}
1812 	/* check to see if this map actually accessable */
1813 	if (access(map->map_file, R_OK) <0)
1814 		return FALSE;
1815 
1816 	/* check to see if this map actually exist */
1817 	if (stat(map->map_file, &sbuf) <0)
1818 	{
1819 		if (tTd(38, 2))
1820 			printf("text_map_open(%s): can not stat %s\n",
1821 				map->map_file, map->map_file);
1822 		return FALSE;
1823 	}
1824 
1825 	if (!S_ISREG(sbuf.st_mode))
1826 	{
1827 		if (tTd(38, 2))
1828 			printf("text_map_open(%s): %s is not a file\n",
1829 				map->map_file, map->map_file);
1830 		return FALSE;
1831 	}
1832 
1833 	if (map->map_keycolnm == NULL)
1834 		map->map_keycolno = 0;
1835 	else
1836 	{
1837 		if (!isdigit(*map->map_keycolnm))
1838 		{
1839 			if (tTd(38, 2))
1840 				printf("text_map_open(%s): -k should specify a number, not %s\n",
1841 					map->map_file, map->map_keycolnm);
1842 			return FALSE;
1843 		}
1844 		map->map_keycolno = atoi(map->map_keycolnm);
1845 	}
1846 
1847 	if (map->map_valcolnm == NULL)
1848 		map->map_valcolno = 0;
1849 	else
1850 	{
1851 		if (!isdigit(*map->map_valcolnm))
1852 		{
1853 			if (tTd(38, 2))
1854 				printf("text_map_open(%s): -v should specify a number, not %s\n",
1855 					map->map_file, map->map_valcolnm);
1856 			return FALSE;
1857 		}
1858 		map->map_valcolno = atoi(map->map_valcolnm);
1859 	}
1860 
1861 	if (tTd(38, 2))
1862 	{
1863 		printf("text_map_open(%s): delimiter = ",
1864 			map->map_file);
1865 		if (map->map_coldelim == '\0')
1866 			printf("(white space)\n");
1867 		else
1868 			printf("%c\n", map->map_coldelim);
1869 	}
1870 
1871 	return TRUE;
1872 }
1873 
1874 
1875 /*
1876 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1877 */
1878 
1879 char *
1880 text_map_lookup(map, name, av, statp)
1881 	MAP *map;
1882 	char *name;
1883 	char **av;
1884 	int *statp;
1885 {
1886 	char *vp;
1887 	auto int vsize;
1888 	int buflen;
1889 	char search_key[MAXNAME + 1];
1890 	char linebuf[MAXLINE];
1891 	FILE *f;
1892 	char buf[MAXNAME + 1];
1893 	char delim;
1894 	int key_idx;
1895 	bool found_it;
1896 	extern char *get_column();
1897 
1898 
1899 	found_it = FALSE;
1900 	if (tTd(38, 20))
1901 		printf("text_map_lookup(%s)\n", name);
1902 
1903 	buflen = strlen(name);
1904 	if (buflen > sizeof search_key - 1)
1905 		buflen = sizeof search_key - 1;
1906 	bcopy(name, search_key, buflen + 1);
1907 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1908 		makelower(search_key);
1909 
1910 	f = fopen(map->map_file, "r");
1911 	if (f == NULL)
1912 	{
1913 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
1914 		*statp = EX_UNAVAILABLE;
1915 		return NULL;
1916 	}
1917 	key_idx = map->map_keycolno;
1918 	delim = map->map_coldelim;
1919 	while (fgets(linebuf, MAXLINE, f))
1920 	{
1921 		char *lf;
1922 		if (linebuf[0] == '#')
1923 			continue; /* skip comment line */
1924 		if (lf = strchr(linebuf, '\n'))
1925 			*lf = '\0';
1926 		if (!strcasecmp(search_key,
1927 				get_column(linebuf, key_idx, delim, buf)))
1928 		{
1929 			found_it = TRUE;
1930 			break;
1931 		}
1932 	}
1933 	fclose(f);
1934 	if (!found_it)
1935 	{
1936 #ifdef MAP_EXIT_STAT
1937 		*statp = EX_NOTFOUND;
1938 #endif
1939 		return(NULL);
1940 	}
1941 	vp = get_column(linebuf, map->map_valcolno, delim, buf);
1942 	vsize = strlen(vp);
1943 #ifdef MAP_EXIT_STAT
1944 	*statp = EX_OK;
1945 #endif
1946 	if (bitset(MF_MATCHONLY, map->map_mflags))
1947 		return map_rewrite(map, name, strlen(name), NULL);
1948 	else
1949 		return map_rewrite(map, vp, vsize, av);
1950 }
1951 
1952 
1953 /*
1954 **  TEXT_GETCANONNAME -- look up canonical name in hosts file
1955 */
1956 
1957 bool
1958 text_getcanonname(name, hbsize, statp)
1959 	char *name;
1960 	int hbsize;
1961 	int *statp;
1962 {
1963 	int buflen;
1964 	int key_idx;
1965 	char *cname;
1966 	bool found;
1967 	char *domain;
1968 	FILE *f;
1969 	char linebuf[MAXLINE];
1970 	char cbuf[MAXNAME + 1];
1971 	char buf[MAXNAME + 1];
1972 	extern char *get_column();
1973 
1974 	shorten_hostname(name);
1975 
1976 	/* we only accept single token search key */
1977 	if (strchr(name, '.') != NULL)
1978 	{
1979 		*statp = EX_NOHOST;
1980 		return FALSE;
1981 	}
1982 
1983 	found = FALSE;
1984 
1985 	f = fopen(HostsFile, "r");
1986 	if (f == NULL)
1987 	{
1988 #ifdef MAP_EXIT_STAT
1989 		*statp = EX_UNAVAILABLE;
1990 #endif
1991 		return FALSE;
1992 	}
1993 	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
1994 	{
1995 		char *p;
1996 
1997 		if (linebuf[0] == '#')
1998 			continue;
1999 		if ((p = strchr(linebuf, '\n')) != NULL)
2000 			*p = '\0';
2001 		cname = get_column(linebuf, 1, '\0', cbuf);
2002 		if (cname != NULL && strcasecmp(name,  cname) == 0)
2003 		{
2004 			found = TRUE;
2005 			break;
2006 		}
2007 
2008 		key_idx = 2;
2009 		while ((p = get_column(linebuf, key_idx, '\0', buf)) != NULL)
2010 		{
2011 			if (strcasecmp(name, p) == 0)
2012 			{
2013 				found = TRUE;
2014 				break;
2015 			}
2016 			key_idx++;
2017 		}
2018 	}
2019 	fclose(f);
2020 	if (!found)
2021 	{
2022 		*statp = EX_NOHOST;
2023 		return FALSE;
2024 	}
2025 
2026 	if (hbsize >= strlen(cname))
2027 	{
2028 		strcpy(name, cname);
2029 		*statp = EX_OK;
2030 		return TRUE;
2031 	}
2032 	*statp = EX_UNAVAILABLE;
2033 	return FALSE;
2034 }
2035 /*
2036 **  STAB (Symbol Table) Modules
2037 */
2038 
2039 
2040 /*
2041 **  STAB_MAP_LOOKUP -- look up alias in symbol table
2042 */
2043 
2044 char *
2045 stab_map_lookup(map, name, av, pstat)
2046 	register MAP *map;
2047 	char *name;
2048 	char **av;
2049 	int *pstat;
2050 {
2051 	register STAB *s;
2052 
2053 	if (tTd(38, 20))
2054 		printf("stab_lookup(%s, %s)\n",
2055 			map->map_mname, name);
2056 
2057 	s = stab(name, ST_ALIAS, ST_FIND);
2058 	if (s != NULL)
2059 		return (s->s_alias);
2060 	return (NULL);
2061 }
2062 
2063 
2064 /*
2065 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
2066 */
2067 
2068 void
2069 stab_map_store(map, lhs, rhs)
2070 	register MAP *map;
2071 	char *lhs;
2072 	char *rhs;
2073 {
2074 	register STAB *s;
2075 
2076 	s = stab(lhs, ST_ALIAS, ST_ENTER);
2077 	s->s_alias = newstr(rhs);
2078 }
2079 
2080 
2081 /*
2082 **  STAB_MAP_OPEN -- initialize (reads data file)
2083 **
2084 **	This is a wierd case -- it is only intended as a fallback for
2085 **	aliases.  For this reason, opens for write (only during a
2086 **	"newaliases") always fails, and opens for read open the
2087 **	actual underlying text file instead of the database.
2088 */
2089 
2090 bool
2091 stab_map_open(map, mode)
2092 	register MAP *map;
2093 	int mode;
2094 {
2095 	FILE *af;
2096 	struct stat st;
2097 
2098 	if (tTd(38, 2))
2099 		printf("stab_map_open(%s, %s)\n",
2100 			map->map_mname, map->map_file);
2101 
2102 	if (mode != O_RDONLY)
2103 	{
2104 		errno = ENODEV;
2105 		return FALSE;
2106 	}
2107 
2108 	af = fopen(map->map_file, "r");
2109 	if (af == NULL)
2110 		return FALSE;
2111 	readaliases(map, af, FALSE, FALSE);
2112 
2113 	if (fstat(fileno(af), &st) >= 0)
2114 		map->map_mtime = st.st_mtime;
2115 	fclose(af);
2116 
2117 	return TRUE;
2118 }
2119 /*
2120 **  Implicit Modules
2121 **
2122 **	Tries several types.  For back compatibility of aliases.
2123 */
2124 
2125 
2126 /*
2127 **  IMPL_MAP_LOOKUP -- lookup in best open database
2128 */
2129 
2130 char *
2131 impl_map_lookup(map, name, av, pstat)
2132 	MAP *map;
2133 	char *name;
2134 	char **av;
2135 	int *pstat;
2136 {
2137 	if (tTd(38, 20))
2138 		printf("impl_map_lookup(%s, %s)\n",
2139 			map->map_mname, name);
2140 
2141 #ifdef NEWDB
2142 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2143 		return db_map_lookup(map, name, av, pstat);
2144 #endif
2145 #ifdef NDBM
2146 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2147 		return ndbm_map_lookup(map, name, av, pstat);
2148 #endif
2149 	return stab_map_lookup(map, name, av, pstat);
2150 }
2151 
2152 /*
2153 **  IMPL_MAP_STORE -- store in open databases
2154 */
2155 
2156 void
2157 impl_map_store(map, lhs, rhs)
2158 	MAP *map;
2159 	char *lhs;
2160 	char *rhs;
2161 {
2162 #ifdef NEWDB
2163 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2164 		db_map_store(map, lhs, rhs);
2165 #endif
2166 #ifdef NDBM
2167 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2168 		ndbm_map_store(map, lhs, rhs);
2169 #endif
2170 	stab_map_store(map, lhs, rhs);
2171 }
2172 
2173 /*
2174 **  IMPL_MAP_OPEN -- implicit database open
2175 */
2176 
2177 bool
2178 impl_map_open(map, mode)
2179 	MAP *map;
2180 	int mode;
2181 {
2182 	struct stat stb;
2183 
2184 	if (tTd(38, 2))
2185 		printf("impl_map_open(%s, %s, %d)\n",
2186 			map->map_mname, map->map_file, mode);
2187 
2188 	if (stat(map->map_file, &stb) < 0)
2189 	{
2190 		/* no alias file at all */
2191 		if (tTd(38, 3))
2192 			printf("no map file\n");
2193 		return FALSE;
2194 	}
2195 
2196 #ifdef NEWDB
2197 	map->map_mflags |= MF_IMPL_HASH;
2198 	if (hash_map_open(map, mode))
2199 	{
2200 #if defined(NDBM) && defined(NIS)
2201 		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
2202 #endif
2203 			return TRUE;
2204 	}
2205 	else
2206 		map->map_mflags &= ~MF_IMPL_HASH;
2207 #endif
2208 #ifdef NDBM
2209 	map->map_mflags |= MF_IMPL_NDBM;
2210 	if (ndbm_map_open(map, mode))
2211 	{
2212 		return TRUE;
2213 	}
2214 	else
2215 		map->map_mflags &= ~MF_IMPL_NDBM;
2216 #endif
2217 
2218 #if defined(NEWDB) || defined(NDBM)
2219 	if (Verbose)
2220 		message("WARNING: cannot open alias database %s", map->map_file);
2221 #else
2222 	if (mode != O_RDONLY)
2223 		usrerr("Cannot rebuild aliases: no database format defined");
2224 #endif
2225 
2226 	return stab_map_open(map, mode);
2227 }
2228 
2229 
2230 /*
2231 **  IMPL_MAP_CLOSE -- close any open database(s)
2232 */
2233 
2234 void
2235 impl_map_close(map)
2236 	MAP *map;
2237 {
2238 	if (tTd(38, 20))
2239 		printf("impl_map_close(%s, %s, %x)\n",
2240 			map->map_mname, map->map_file, map->map_mflags);
2241 #ifdef NEWDB
2242 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2243 	{
2244 		db_map_close(map);
2245 		map->map_mflags &= ~MF_IMPL_HASH;
2246 	}
2247 #endif
2248 
2249 #ifdef NDBM
2250 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2251 	{
2252 		ndbm_map_close(map);
2253 		map->map_mflags &= ~MF_IMPL_NDBM;
2254 	}
2255 #endif
2256 }
2257 /*
2258 **  User map class.
2259 **
2260 **	Provides access to the system password file.
2261 */
2262 
2263 /*
2264 **  USER_MAP_OPEN -- open user map
2265 **
2266 **	Really just binds field names to field numbers.
2267 */
2268 
2269 bool
2270 user_map_open(map, mode)
2271 	MAP *map;
2272 	int mode;
2273 {
2274 	if (tTd(38, 2))
2275 		printf("user_map_open(%s)\n", map->map_mname);
2276 
2277 	if (mode != O_RDONLY)
2278 	{
2279 		/* issue a pseudo-error message */
2280 #ifdef ENOSYS
2281 		errno = ENOSYS;
2282 #else
2283 # ifdef EFTYPE
2284 		errno = EFTYPE;
2285 # else
2286 		errno = ENXIO;
2287 # endif
2288 #endif
2289 		return FALSE;
2290 	}
2291 	if (map->map_valcolnm == NULL)
2292 		/* nothing */ ;
2293 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
2294 		map->map_valcolno = 1;
2295 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
2296 		map->map_valcolno = 2;
2297 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
2298 		map->map_valcolno = 3;
2299 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
2300 		map->map_valcolno = 4;
2301 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
2302 		map->map_valcolno = 5;
2303 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2304 		map->map_valcolno = 6;
2305 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2306 		map->map_valcolno = 7;
2307 	else
2308 	{
2309 		syserr("User map %s: unknown column name %s",
2310 			map->map_mname, map->map_valcolnm);
2311 		return FALSE;
2312 	}
2313 	return TRUE;
2314 }
2315 
2316 
2317 /*
2318 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2319 */
2320 
2321 char *
2322 user_map_lookup(map, key, av, statp)
2323 	MAP *map;
2324 	char *key;
2325 	char **av;
2326 	int *statp;
2327 {
2328 	struct passwd *pw;
2329 
2330 	if (tTd(38, 20))
2331 		printf("user_map_lookup(%s, %s)\n",
2332 			map->map_mname, key);
2333 
2334 	pw = sm_getpwnam(key);
2335 	if (pw == NULL)
2336 		return NULL;
2337 	if (bitset(MF_MATCHONLY, map->map_mflags))
2338 		return map_rewrite(map, key, strlen(key), NULL);
2339 	else
2340 	{
2341 		char *rwval = NULL;
2342 		char buf[30];
2343 
2344 		switch (map->map_valcolno)
2345 		{
2346 		  case 0:
2347 		  case 1:
2348 			rwval = pw->pw_name;
2349 			break;
2350 
2351 		  case 2:
2352 			rwval = pw->pw_passwd;
2353 			break;
2354 
2355 		  case 3:
2356 			sprintf(buf, "%d", pw->pw_uid);
2357 			rwval = buf;
2358 			break;
2359 
2360 		  case 4:
2361 			sprintf(buf, "%d", pw->pw_gid);
2362 			rwval = buf;
2363 			break;
2364 
2365 		  case 5:
2366 			rwval = pw->pw_gecos;
2367 			break;
2368 
2369 		  case 6:
2370 			rwval = pw->pw_dir;
2371 			break;
2372 
2373 		  case 7:
2374 			rwval = pw->pw_shell;
2375 			break;
2376 		}
2377 		return map_rewrite(map, rwval, strlen(rwval), av);
2378 	}
2379 }
2380 /*
2381 **  BESTMX -- find the best MX for a name
2382 **
2383 **	This is really a hack, but I don't see any obvious way
2384 **	to generalize it at the moment.
2385 */
2386 
2387 #if NAMED_BIND
2388 
2389 char *
2390 bestmx_map_lookup(map, name, av, statp)
2391 	MAP *map;
2392 	char *name;
2393 	char **av;
2394 	int *statp;
2395 {
2396         int nmx;
2397         auto int rcode;
2398         char *mxhosts[MAXMXHOSTS + 1];
2399 
2400 	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
2401 	if (nmx <= 0)
2402 		return NULL;
2403 	if (bitset(MF_MATCHONLY, map->map_mflags))
2404 		return map_rewrite(map, name, strlen(name), NULL);
2405 	else
2406 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
2407 }
2408 
2409 #endif
2410 /*
2411 **  Program map type.
2412 **
2413 **	This provides access to arbitrary programs.  It should be used
2414 **	only very sparingly, since there is no way to bound the cost
2415 **	of invoking an arbitrary program.
2416 */
2417 
2418 char *
2419 prog_map_lookup(map, name, av, statp)
2420 	MAP *map;
2421 	char *name;
2422 	char **av;
2423 	int *statp;
2424 {
2425 	int i;
2426 	register char *p;
2427 	int fd;
2428 	auto pid_t pid;
2429 	char *argv[MAXPV + 1];
2430 	char buf[MAXLINE];
2431 
2432 	if (tTd(38, 20))
2433 		printf("prog_map_lookup(%s, %s) %s\n",
2434 			map->map_mname, name, map->map_file);
2435 
2436 	i = 0;
2437 	argv[i++] = map->map_file;
2438 	strcpy(buf, map->map_rebuild);
2439 	for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
2440 	{
2441 		if (i >= MAXPV - 1)
2442 			break;
2443 		argv[i++] = p;
2444 	}
2445 	argv[i++] = name;
2446 	argv[i] = NULL;
2447 	pid = prog_open(argv, &fd, CurEnv);
2448 	if (pid < 0)
2449 	{
2450 		if (tTd(38, 9))
2451 			printf("prog_map_lookup(%s) failed (%s) -- closing",
2452 				map->map_mname, errstring(errno));
2453 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
2454 		return NULL;
2455 	}
2456 	i = read(fd, buf, sizeof buf - 1);
2457 	if (i <= 0 && tTd(38, 2))
2458 		printf("prog_map_lookup(%s): read error %s\n",
2459 			map->map_mname, errstring(errno));
2460 	if (i > 0)
2461 	{
2462 		char *rval;
2463 
2464 		buf[i] = '\0';
2465 		p = strchr(buf, '\n');
2466 		if (p != NULL)
2467 			*p = '\0';
2468 
2469 		/* collect the return value */
2470 		if (bitset(MF_MATCHONLY, map->map_mflags))
2471 			rval = map_rewrite(map, name, strlen(name), NULL);
2472 		else
2473 			rval = map_rewrite(map, buf, strlen(buf), NULL);
2474 
2475 		/* now flush any additional output */
2476 		while ((i = read(fd, buf, sizeof buf)) > 0)
2477 			continue;
2478 		close(fd);
2479 
2480 		/* and wait for the process to terminate */
2481 		*statp = waitfor(pid);
2482 
2483 		return rval;
2484 	}
2485 
2486 	close(fd);
2487 	*statp = waitfor(pid);
2488 	return NULL;
2489 }
2490 /*
2491 **  Sequenced map type.
2492 **
2493 **	Tries each map in order until something matches, much like
2494 **	implicit.  Stores go to the first map in the list that can
2495 **	support storing.
2496 **
2497 **	This is slightly unusual in that there are two interfaces.
2498 **	The "sequence" interface lets you stack maps arbitrarily.
2499 **	The "switch" interface builds a sequence map by looking
2500 **	at a system-dependent configuration file such as
2501 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2502 **
2503 **	We don't need an explicit open, since all maps are
2504 **	opened during startup, including underlying maps.
2505 */
2506 
2507 /*
2508 **  SEQ_MAP_PARSE -- Sequenced map parsing
2509 */
2510 
2511 bool
2512 seq_map_parse(map, ap)
2513 	MAP *map;
2514 	char *ap;
2515 {
2516 	int maxmap;
2517 
2518 	if (tTd(38, 2))
2519 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2520 	maxmap = 0;
2521 	while (*ap != '\0')
2522 	{
2523 		register char *p;
2524 		STAB *s;
2525 
2526 		/* find beginning of map name */
2527 		while (isascii(*ap) && isspace(*ap))
2528 			ap++;
2529 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2530 			continue;
2531 		if (*p != '\0')
2532 			*p++ = '\0';
2533 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2534 			p++;
2535 		if (*ap == '\0')
2536 		{
2537 			ap = p;
2538 			continue;
2539 		}
2540 		s = stab(ap, ST_MAP, ST_FIND);
2541 		if (s == NULL)
2542 		{
2543 			syserr("Sequence map %s: unknown member map %s",
2544 				map->map_mname, ap);
2545 		}
2546 		else if (maxmap == MAXMAPSTACK)
2547 		{
2548 			syserr("Sequence map %s: too many member maps (%d max)",
2549 				map->map_mname, MAXMAPSTACK);
2550 			maxmap++;
2551 		}
2552 		else if (maxmap < MAXMAPSTACK)
2553 		{
2554 			map->map_stack[maxmap++] = &s->s_map;
2555 		}
2556 		ap = p;
2557 	}
2558 	return TRUE;
2559 }
2560 
2561 
2562 /*
2563 **  SWITCH_MAP_OPEN -- open a switched map
2564 **
2565 **	This looks at the system-dependent configuration and builds
2566 **	a sequence map that does the same thing.
2567 **
2568 **	Every system must define a switch_map_find routine in conf.c
2569 **	that will return the list of service types associated with a
2570 **	given service class.
2571 */
2572 
2573 bool
2574 switch_map_open(map, mode)
2575 	MAP *map;
2576 	int mode;
2577 {
2578 	int mapno;
2579 	int nmaps;
2580 	char *maptype[MAXMAPSTACK];
2581 
2582 	if (tTd(38, 2))
2583 		printf("switch_map_open(%s, %s, %d)\n",
2584 			map->map_mname, map->map_file, mode);
2585 
2586 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2587 	if (tTd(38, 19))
2588 	{
2589 		printf("\tswitch_map_find => %d\n", nmaps);
2590 		for (mapno = 0; mapno < nmaps; mapno++)
2591 			printf("\t\t%s\n", maptype[mapno]);
2592 	}
2593 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2594 		return FALSE;
2595 
2596 	for (mapno = 0; mapno < nmaps; mapno++)
2597 	{
2598 		register STAB *s;
2599 		char nbuf[MAXNAME + 1];
2600 
2601 		if (maptype[mapno] == NULL)
2602 			continue;
2603 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2604 		s = stab(nbuf, ST_MAP, ST_FIND);
2605 		if (s == NULL)
2606 		{
2607 			syserr("Switch map %s: unknown member map %s",
2608 				map->map_mname, nbuf);
2609 		}
2610 		else
2611 		{
2612 			map->map_stack[mapno] = &s->s_map;
2613 			if (tTd(38, 4))
2614 				printf("\tmap_stack[%d] = %s:%s\n",
2615 					mapno, s->s_map.map_class->map_cname,
2616 					nbuf);
2617 		}
2618 	}
2619 	return TRUE;
2620 }
2621 
2622 
2623 /*
2624 **  SEQ_MAP_CLOSE -- close all underlying maps
2625 */
2626 
2627 seq_map_close(map)
2628 	MAP *map;
2629 {
2630 	int mapno;
2631 
2632 	if (tTd(38, 20))
2633 		printf("seq_map_close(%s)\n", map->map_mname);
2634 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2635 	{
2636 		MAP *mm = map->map_stack[mapno];
2637 
2638 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2639 			continue;
2640 		mm->map_class->map_close(mm);
2641 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
2642 	}
2643 }
2644 
2645 
2646 /*
2647 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2648 */
2649 
2650 char *
2651 seq_map_lookup(map, key, args, pstat)
2652 	MAP *map;
2653 	char *key;
2654 	char **args;
2655 	int *pstat;
2656 {
2657 	int mapno;
2658 	int mapbit = 0x01;
2659 
2660 	if (tTd(38, 20))
2661 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2662 
2663 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2664 	{
2665 		MAP *mm = map->map_stack[mapno];
2666 		int stat = 0;
2667 		char *rv;
2668 
2669 		if (mm == NULL)
2670 			continue;
2671 		if (!bitset(MF_OPEN, mm->map_mflags))
2672 		{
2673 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2674 			{
2675 				*pstat = EX_UNAVAILABLE;
2676 				return NULL;
2677 			}
2678 			continue;
2679 		}
2680 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2681 		if (rv != NULL)
2682 			return rv;
2683 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2684 			return NULL;
2685 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2686 		{
2687 			*pstat = stat;
2688 			return NULL;
2689 		}
2690 	}
2691 	return NULL;
2692 }
2693 
2694 
2695 /*
2696 **  SEQ_MAP_STORE -- sequenced map store
2697 */
2698 
2699 void
2700 seq_map_store(map, key, val)
2701 	MAP *map;
2702 	char *key;
2703 	char *val;
2704 {
2705 	int mapno;
2706 
2707 	if (tTd(38, 12))
2708 		printf("seq_map_store(%s, %s, %s)\n",
2709 			map->map_mname, key, val);
2710 
2711 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2712 	{
2713 		MAP *mm = map->map_stack[mapno];
2714 
2715 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2716 			continue;
2717 
2718 		mm->map_class->map_store(mm, key, val);
2719 		return;
2720 	}
2721 	syserr("seq_map_store(%s, %s, %s): no writable map",
2722 		map->map_mname, key, val);
2723 }
2724 /*
2725 **  GETCANONNAME -- look up name using service switch
2726 **
2727 **	Parameters:
2728 **		host -- the host name to look up.
2729 **		hbsize -- the size of the host buffer.
2730 **		trymx -- if set, try MX records.
2731 **
2732 **	Returns:
2733 **		TRUE -- if the host was found.
2734 **		FALSE -- otherwise.
2735 */
2736 
2737 bool
2738 getcanonname(host, hbsize, trymx)
2739 	char *host;
2740 	int hbsize;
2741 	bool trymx;
2742 {
2743 	int nmaps;
2744 	int mapno;
2745 	bool found = FALSE;
2746 	auto int stat;
2747 	char *maptype[MAXMAPSTACK];
2748 	short mapreturn[MAXMAPACTIONS];
2749 	extern int h_errno;
2750 
2751 	nmaps = switch_map_find("hosts", maptype, mapreturn);
2752 	for (mapno = 0; mapno < nmaps; mapno++)
2753 	{
2754 		int i;
2755 
2756 		if (tTd(38, 20))
2757 			printf("getcanonname(%s), trying %s\n",
2758 				host, maptype[mapno]);
2759 		if (strcmp("files", maptype[mapno]) == 0)
2760 			found = text_getcanonname(host, hbsize, &stat);
2761 #ifdef NIS
2762 		else if (strcmp("nis", maptype[mapno]) == 0)
2763 			found = nis_getcanonname(host, hbsize, &stat);
2764 #endif
2765 #ifdef NISPLUS
2766 		else if (strcmp("nisplus", maptype[mapno]) == 0)
2767 			found = nisplus_getcanonname(host, hbsize, &stat);
2768 #endif
2769 #if NAMED_BIND
2770 		else if (strcmp("dns", maptype[mapno]) == 0)
2771 			found = dns_getcanonname(host, hbsize, trymx, &stat);
2772 #endif
2773 		else
2774 		{
2775 			found = FALSE;
2776 			stat = EX_UNAVAILABLE;
2777 		}
2778 		if (found)
2779 			break;
2780 
2781 		/* see if we should continue */
2782 		if (stat == EX_TEMPFAIL)
2783 			i = MA_TRYAGAIN;
2784 		else if (stat == EX_NOHOST)
2785 			i = MA_NOTFOUND;
2786 		else
2787 			i = MA_UNAVAIL;
2788 		if (bitset(1 << mapno, mapreturn[i]))
2789 			break;
2790 	}
2791 
2792 	if (found)
2793 	{
2794 		char *d;
2795 
2796 		if (tTd(38, 20))
2797 			printf("getcanonname(%s), found\n", host);
2798 
2799 		/*
2800 		**  If returned name is still single token, compensate
2801 		**  by tagging on $m.  This is because some sites set
2802 		**  up their DNS or NIS databases wrong.
2803 		*/
2804 
2805 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
2806 		{
2807 			d = macvalue('m', CurEnv);
2808 			if (d != NULL &&
2809 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
2810 			{
2811 				if (host[strlen(host) - 1] != '.')
2812 					strcat(host, ".");
2813 				strcat(host, d);
2814 			}
2815 			else
2816 			{
2817 				return FALSE;
2818 			}
2819 		}
2820 		return TRUE;
2821 	}
2822 
2823 	if (tTd(38, 20))
2824 		printf("getcanonname(%s), failed, stat=%d\n", host, stat);
2825 
2826 #if NAMED_BIND
2827 	if (stat == EX_NOHOST)
2828 		h_errno = HOST_NOT_FOUND;
2829 	else
2830 		h_errno = TRY_AGAIN;
2831 #endif
2832 
2833 	return FALSE;
2834 }
2835 /*
2836 **  NULL stubs
2837 */
2838 
2839 bool
2840 null_map_open(map, mode)
2841 	MAP *map;
2842 	int mode;
2843 {
2844 	return TRUE;
2845 }
2846 
2847 void
2848 null_map_close(map)
2849 	MAP *map;
2850 {
2851 	return;
2852 }
2853 
2854 void
2855 null_map_store(map, key, val)
2856 	MAP *map;
2857 	char *key;
2858 	char *val;
2859 {
2860 	return;
2861 }
2862 
2863 
2864 /*
2865 **  BOGUS stubs
2866 */
2867 
2868 char *
2869 bogus_map_lookup(map, key, args, pstat)
2870 	MAP *map;
2871 	char *key;
2872 	char **args;
2873 	int *pstat;
2874 {
2875 	*pstat = EX_TEMPFAIL;
2876 	return NULL;
2877 }
2878 
2879 MAPCLASS	BogusMapClass =
2880 {
2881 	"bogus-map",		NULL,		0,
2882 	NULL,		bogus_map_lookup,	null_map_store,
2883 	null_map_open,	null_map_close,
2884 };
2885