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