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