xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision dbfb0019)
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.69 (Berkeley) 05/28/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 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 key_idx;
1988 	char *cname;
1989 	bool found;
1990 	FILE *f;
1991 	char linebuf[MAXLINE];
1992 	char cbuf[MAXNAME + 1];
1993 	char buf[MAXNAME + 1];
1994 	extern char *get_column();
1995 
1996 	shorten_hostname(name);
1997 
1998 	/* we only accept single token search key */
1999 	if (strchr(name, '.') != NULL)
2000 	{
2001 		*statp = EX_NOHOST;
2002 		return FALSE;
2003 	}
2004 
2005 	found = FALSE;
2006 
2007 	f = fopen(HostsFile, "r");
2008 	if (f == NULL)
2009 	{
2010 #ifdef MAP_EXIT_STAT
2011 		*statp = EX_UNAVAILABLE;
2012 #endif
2013 		return FALSE;
2014 	}
2015 	while (!found && fgets(linebuf, MAXLINE, f) != NULL)
2016 	{
2017 		char *p;
2018 
2019 		if (linebuf[0] == '#')
2020 			continue;
2021 		if ((p = strchr(linebuf, '\n')) != NULL)
2022 			*p = '\0';
2023 		cname = get_column(linebuf, 1, '\0', cbuf);
2024 		if (cname != NULL && strcasecmp(name,  cname) == 0)
2025 		{
2026 			found = TRUE;
2027 			break;
2028 		}
2029 
2030 		key_idx = 2;
2031 		while ((p = get_column(linebuf, key_idx, '\0', buf)) != NULL)
2032 		{
2033 			if (strcasecmp(name, p) == 0)
2034 			{
2035 				found = TRUE;
2036 				break;
2037 			}
2038 			key_idx++;
2039 		}
2040 	}
2041 	fclose(f);
2042 	if (!found)
2043 	{
2044 		*statp = EX_NOHOST;
2045 		return FALSE;
2046 	}
2047 
2048 	if (hbsize >= strlen(cname))
2049 	{
2050 		strcpy(name, cname);
2051 		*statp = EX_OK;
2052 		return TRUE;
2053 	}
2054 	*statp = EX_UNAVAILABLE;
2055 	return FALSE;
2056 }
2057 /*
2058 **  STAB (Symbol Table) Modules
2059 */
2060 
2061 
2062 /*
2063 **  STAB_MAP_LOOKUP -- look up alias in symbol table
2064 */
2065 
2066 char *
2067 stab_map_lookup(map, name, av, pstat)
2068 	register MAP *map;
2069 	char *name;
2070 	char **av;
2071 	int *pstat;
2072 {
2073 	register STAB *s;
2074 
2075 	if (tTd(38, 20))
2076 		printf("stab_lookup(%s, %s)\n",
2077 			map->map_mname, name);
2078 
2079 	s = stab(name, ST_ALIAS, ST_FIND);
2080 	if (s != NULL)
2081 		return (s->s_alias);
2082 	return (NULL);
2083 }
2084 
2085 
2086 /*
2087 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
2088 */
2089 
2090 void
2091 stab_map_store(map, lhs, rhs)
2092 	register MAP *map;
2093 	char *lhs;
2094 	char *rhs;
2095 {
2096 	register STAB *s;
2097 
2098 	s = stab(lhs, ST_ALIAS, ST_ENTER);
2099 	s->s_alias = newstr(rhs);
2100 }
2101 
2102 
2103 /*
2104 **  STAB_MAP_OPEN -- initialize (reads data file)
2105 **
2106 **	This is a wierd case -- it is only intended as a fallback for
2107 **	aliases.  For this reason, opens for write (only during a
2108 **	"newaliases") always fails, and opens for read open the
2109 **	actual underlying text file instead of the database.
2110 */
2111 
2112 bool
2113 stab_map_open(map, mode)
2114 	register MAP *map;
2115 	int mode;
2116 {
2117 	FILE *af;
2118 	struct stat st;
2119 
2120 	if (tTd(38, 2))
2121 		printf("stab_map_open(%s, %s)\n",
2122 			map->map_mname, map->map_file);
2123 
2124 	if (mode != O_RDONLY)
2125 	{
2126 		errno = ENODEV;
2127 		return FALSE;
2128 	}
2129 
2130 	af = fopen(map->map_file, "r");
2131 	if (af == NULL)
2132 		return FALSE;
2133 	readaliases(map, af, FALSE, FALSE);
2134 
2135 	if (fstat(fileno(af), &st) >= 0)
2136 		map->map_mtime = st.st_mtime;
2137 	fclose(af);
2138 
2139 	return TRUE;
2140 }
2141 /*
2142 **  Implicit Modules
2143 **
2144 **	Tries several types.  For back compatibility of aliases.
2145 */
2146 
2147 
2148 /*
2149 **  IMPL_MAP_LOOKUP -- lookup in best open database
2150 */
2151 
2152 char *
2153 impl_map_lookup(map, name, av, pstat)
2154 	MAP *map;
2155 	char *name;
2156 	char **av;
2157 	int *pstat;
2158 {
2159 	if (tTd(38, 20))
2160 		printf("impl_map_lookup(%s, %s)\n",
2161 			map->map_mname, name);
2162 
2163 #ifdef NEWDB
2164 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2165 		return db_map_lookup(map, name, av, pstat);
2166 #endif
2167 #ifdef NDBM
2168 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2169 		return ndbm_map_lookup(map, name, av, pstat);
2170 #endif
2171 	return stab_map_lookup(map, name, av, pstat);
2172 }
2173 
2174 /*
2175 **  IMPL_MAP_STORE -- store in open databases
2176 */
2177 
2178 void
2179 impl_map_store(map, lhs, rhs)
2180 	MAP *map;
2181 	char *lhs;
2182 	char *rhs;
2183 {
2184 #ifdef NEWDB
2185 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2186 		db_map_store(map, lhs, rhs);
2187 #endif
2188 #ifdef NDBM
2189 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2190 		ndbm_map_store(map, lhs, rhs);
2191 #endif
2192 	stab_map_store(map, lhs, rhs);
2193 }
2194 
2195 /*
2196 **  IMPL_MAP_OPEN -- implicit database open
2197 */
2198 
2199 bool
2200 impl_map_open(map, mode)
2201 	MAP *map;
2202 	int mode;
2203 {
2204 	struct stat stb;
2205 
2206 	if (tTd(38, 2))
2207 		printf("impl_map_open(%s, %s, %d)\n",
2208 			map->map_mname, map->map_file, mode);
2209 
2210 	if (stat(map->map_file, &stb) < 0)
2211 	{
2212 		/* no alias file at all */
2213 		if (tTd(38, 3))
2214 			printf("no map file\n");
2215 		return FALSE;
2216 	}
2217 
2218 #ifdef NEWDB
2219 	map->map_mflags |= MF_IMPL_HASH;
2220 	if (hash_map_open(map, mode))
2221 	{
2222 #if defined(NDBM) && defined(NIS)
2223 		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
2224 #endif
2225 			return TRUE;
2226 	}
2227 	else
2228 		map->map_mflags &= ~MF_IMPL_HASH;
2229 #endif
2230 #ifdef NDBM
2231 	map->map_mflags |= MF_IMPL_NDBM;
2232 	if (ndbm_map_open(map, mode))
2233 	{
2234 		return TRUE;
2235 	}
2236 	else
2237 		map->map_mflags &= ~MF_IMPL_NDBM;
2238 #endif
2239 
2240 #if defined(NEWDB) || defined(NDBM)
2241 	if (Verbose)
2242 		message("WARNING: cannot open alias database %s", map->map_file);
2243 #else
2244 	if (mode != O_RDONLY)
2245 		usrerr("Cannot rebuild aliases: no database format defined");
2246 #endif
2247 
2248 	return stab_map_open(map, mode);
2249 }
2250 
2251 
2252 /*
2253 **  IMPL_MAP_CLOSE -- close any open database(s)
2254 */
2255 
2256 void
2257 impl_map_close(map)
2258 	MAP *map;
2259 {
2260 	if (tTd(38, 20))
2261 		printf("impl_map_close(%s, %s, %x)\n",
2262 			map->map_mname, map->map_file, map->map_mflags);
2263 #ifdef NEWDB
2264 	if (bitset(MF_IMPL_HASH, map->map_mflags))
2265 	{
2266 		db_map_close(map);
2267 		map->map_mflags &= ~MF_IMPL_HASH;
2268 	}
2269 #endif
2270 
2271 #ifdef NDBM
2272 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
2273 	{
2274 		ndbm_map_close(map);
2275 		map->map_mflags &= ~MF_IMPL_NDBM;
2276 	}
2277 #endif
2278 }
2279 /*
2280 **  User map class.
2281 **
2282 **	Provides access to the system password file.
2283 */
2284 
2285 /*
2286 **  USER_MAP_OPEN -- open user map
2287 **
2288 **	Really just binds field names to field numbers.
2289 */
2290 
2291 bool
2292 user_map_open(map, mode)
2293 	MAP *map;
2294 	int mode;
2295 {
2296 	if (tTd(38, 2))
2297 		printf("user_map_open(%s)\n", map->map_mname);
2298 
2299 	if (mode != O_RDONLY)
2300 	{
2301 		/* issue a pseudo-error message */
2302 #ifdef ENOSYS
2303 		errno = ENOSYS;
2304 #else
2305 # ifdef EFTYPE
2306 		errno = EFTYPE;
2307 # else
2308 		errno = ENXIO;
2309 # endif
2310 #endif
2311 		return FALSE;
2312 	}
2313 	if (map->map_valcolnm == NULL)
2314 		/* nothing */ ;
2315 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
2316 		map->map_valcolno = 1;
2317 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
2318 		map->map_valcolno = 2;
2319 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
2320 		map->map_valcolno = 3;
2321 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
2322 		map->map_valcolno = 4;
2323 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
2324 		map->map_valcolno = 5;
2325 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
2326 		map->map_valcolno = 6;
2327 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
2328 		map->map_valcolno = 7;
2329 	else
2330 	{
2331 		syserr("User map %s: unknown column name %s",
2332 			map->map_mname, map->map_valcolnm);
2333 		return FALSE;
2334 	}
2335 	return TRUE;
2336 }
2337 
2338 
2339 /*
2340 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
2341 */
2342 
2343 char *
2344 user_map_lookup(map, key, av, statp)
2345 	MAP *map;
2346 	char *key;
2347 	char **av;
2348 	int *statp;
2349 {
2350 	struct passwd *pw;
2351 
2352 	if (tTd(38, 20))
2353 		printf("user_map_lookup(%s, %s)\n",
2354 			map->map_mname, key);
2355 
2356 	pw = sm_getpwnam(key);
2357 	if (pw == NULL)
2358 		return NULL;
2359 	if (bitset(MF_MATCHONLY, map->map_mflags))
2360 		return map_rewrite(map, key, strlen(key), NULL);
2361 	else
2362 	{
2363 		char *rwval = NULL;
2364 		char buf[30];
2365 
2366 		switch (map->map_valcolno)
2367 		{
2368 		  case 0:
2369 		  case 1:
2370 			rwval = pw->pw_name;
2371 			break;
2372 
2373 		  case 2:
2374 			rwval = pw->pw_passwd;
2375 			break;
2376 
2377 		  case 3:
2378 			sprintf(buf, "%d", pw->pw_uid);
2379 			rwval = buf;
2380 			break;
2381 
2382 		  case 4:
2383 			sprintf(buf, "%d", pw->pw_gid);
2384 			rwval = buf;
2385 			break;
2386 
2387 		  case 5:
2388 			rwval = pw->pw_gecos;
2389 			break;
2390 
2391 		  case 6:
2392 			rwval = pw->pw_dir;
2393 			break;
2394 
2395 		  case 7:
2396 			rwval = pw->pw_shell;
2397 			break;
2398 		}
2399 		return map_rewrite(map, rwval, strlen(rwval), av);
2400 	}
2401 }
2402 /*
2403 **  BESTMX -- find the best MX for a name
2404 **
2405 **	This is really a hack, but I don't see any obvious way
2406 **	to generalize it at the moment.
2407 */
2408 
2409 #if NAMED_BIND
2410 
2411 char *
2412 bestmx_map_lookup(map, name, av, statp)
2413 	MAP *map;
2414 	char *name;
2415 	char **av;
2416 	int *statp;
2417 {
2418         int nmx;
2419         auto int rcode;
2420         char *mxhosts[MAXMXHOSTS + 1];
2421 
2422 	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
2423 	if (nmx <= 0)
2424 		return NULL;
2425 	if (bitset(MF_MATCHONLY, map->map_mflags))
2426 		return map_rewrite(map, name, strlen(name), NULL);
2427 	else
2428 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
2429 }
2430 
2431 #endif
2432 /*
2433 **  Program map type.
2434 **
2435 **	This provides access to arbitrary programs.  It should be used
2436 **	only very sparingly, since there is no way to bound the cost
2437 **	of invoking an arbitrary program.
2438 */
2439 
2440 char *
2441 prog_map_lookup(map, name, av, statp)
2442 	MAP *map;
2443 	char *name;
2444 	char **av;
2445 	int *statp;
2446 {
2447 	int i;
2448 	register char *p;
2449 	int fd;
2450 	auto pid_t pid;
2451 	char *argv[MAXPV + 1];
2452 	char buf[MAXLINE];
2453 
2454 	if (tTd(38, 20))
2455 		printf("prog_map_lookup(%s, %s) %s\n",
2456 			map->map_mname, name, map->map_file);
2457 
2458 	i = 0;
2459 	argv[i++] = map->map_file;
2460 	strcpy(buf, map->map_rebuild);
2461 	for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
2462 	{
2463 		if (i >= MAXPV - 1)
2464 			break;
2465 		argv[i++] = p;
2466 	}
2467 	argv[i++] = name;
2468 	argv[i] = NULL;
2469 	pid = prog_open(argv, &fd, CurEnv);
2470 	if (pid < 0)
2471 	{
2472 		if (tTd(38, 9))
2473 			printf("prog_map_lookup(%s) failed (%s) -- closing",
2474 				map->map_mname, errstring(errno));
2475 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
2476 		return NULL;
2477 	}
2478 	i = read(fd, buf, sizeof buf - 1);
2479 	if (i <= 0 && tTd(38, 2))
2480 		printf("prog_map_lookup(%s): read error %s\n",
2481 			map->map_mname, errstring(errno));
2482 	if (i > 0)
2483 	{
2484 		char *rval;
2485 
2486 		buf[i] = '\0';
2487 		p = strchr(buf, '\n');
2488 		if (p != NULL)
2489 			*p = '\0';
2490 
2491 		/* collect the return value */
2492 		if (bitset(MF_MATCHONLY, map->map_mflags))
2493 			rval = map_rewrite(map, name, strlen(name), NULL);
2494 		else
2495 			rval = map_rewrite(map, buf, strlen(buf), NULL);
2496 
2497 		/* now flush any additional output */
2498 		while ((i = read(fd, buf, sizeof buf)) > 0)
2499 			continue;
2500 		close(fd);
2501 
2502 		/* and wait for the process to terminate */
2503 		*statp = waitfor(pid);
2504 
2505 		return rval;
2506 	}
2507 
2508 	close(fd);
2509 	*statp = waitfor(pid);
2510 	return NULL;
2511 }
2512 /*
2513 **  Sequenced map type.
2514 **
2515 **	Tries each map in order until something matches, much like
2516 **	implicit.  Stores go to the first map in the list that can
2517 **	support storing.
2518 **
2519 **	This is slightly unusual in that there are two interfaces.
2520 **	The "sequence" interface lets you stack maps arbitrarily.
2521 **	The "switch" interface builds a sequence map by looking
2522 **	at a system-dependent configuration file such as
2523 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
2524 **
2525 **	We don't need an explicit open, since all maps are
2526 **	opened during startup, including underlying maps.
2527 */
2528 
2529 /*
2530 **  SEQ_MAP_PARSE -- Sequenced map parsing
2531 */
2532 
2533 bool
2534 seq_map_parse(map, ap)
2535 	MAP *map;
2536 	char *ap;
2537 {
2538 	int maxmap;
2539 
2540 	if (tTd(38, 2))
2541 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
2542 	maxmap = 0;
2543 	while (*ap != '\0')
2544 	{
2545 		register char *p;
2546 		STAB *s;
2547 
2548 		/* find beginning of map name */
2549 		while (isascii(*ap) && isspace(*ap))
2550 			ap++;
2551 		for (p = ap; isascii(*p) && isalnum(*p); p++)
2552 			continue;
2553 		if (*p != '\0')
2554 			*p++ = '\0';
2555 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
2556 			p++;
2557 		if (*ap == '\0')
2558 		{
2559 			ap = p;
2560 			continue;
2561 		}
2562 		s = stab(ap, ST_MAP, ST_FIND);
2563 		if (s == NULL)
2564 		{
2565 			syserr("Sequence map %s: unknown member map %s",
2566 				map->map_mname, ap);
2567 		}
2568 		else if (maxmap == MAXMAPSTACK)
2569 		{
2570 			syserr("Sequence map %s: too many member maps (%d max)",
2571 				map->map_mname, MAXMAPSTACK);
2572 			maxmap++;
2573 		}
2574 		else if (maxmap < MAXMAPSTACK)
2575 		{
2576 			map->map_stack[maxmap++] = &s->s_map;
2577 		}
2578 		ap = p;
2579 	}
2580 	return TRUE;
2581 }
2582 
2583 
2584 /*
2585 **  SWITCH_MAP_OPEN -- open a switched map
2586 **
2587 **	This looks at the system-dependent configuration and builds
2588 **	a sequence map that does the same thing.
2589 **
2590 **	Every system must define a switch_map_find routine in conf.c
2591 **	that will return the list of service types associated with a
2592 **	given service class.
2593 */
2594 
2595 bool
2596 switch_map_open(map, mode)
2597 	MAP *map;
2598 	int mode;
2599 {
2600 	int mapno;
2601 	int nmaps;
2602 	char *maptype[MAXMAPSTACK];
2603 
2604 	if (tTd(38, 2))
2605 		printf("switch_map_open(%s, %s, %d)\n",
2606 			map->map_mname, map->map_file, mode);
2607 
2608 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
2609 	if (tTd(38, 19))
2610 	{
2611 		printf("\tswitch_map_find => %d\n", nmaps);
2612 		for (mapno = 0; mapno < nmaps; mapno++)
2613 			printf("\t\t%s\n", maptype[mapno]);
2614 	}
2615 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
2616 		return FALSE;
2617 
2618 	for (mapno = 0; mapno < nmaps; mapno++)
2619 	{
2620 		register STAB *s;
2621 		char nbuf[MAXNAME + 1];
2622 
2623 		if (maptype[mapno] == NULL)
2624 			continue;
2625 		(void) sprintf(nbuf, "%s.%s", map->map_file, maptype[mapno]);
2626 		s = stab(nbuf, ST_MAP, ST_FIND);
2627 		if (s == NULL)
2628 		{
2629 			syserr("Switch map %s: unknown member map %s",
2630 				map->map_mname, nbuf);
2631 		}
2632 		else
2633 		{
2634 			map->map_stack[mapno] = &s->s_map;
2635 			if (tTd(38, 4))
2636 				printf("\tmap_stack[%d] = %s:%s\n",
2637 					mapno, s->s_map.map_class->map_cname,
2638 					nbuf);
2639 		}
2640 	}
2641 	return TRUE;
2642 }
2643 
2644 
2645 /*
2646 **  SEQ_MAP_CLOSE -- close all underlying maps
2647 */
2648 
2649 void
2650 seq_map_close(map)
2651 	MAP *map;
2652 {
2653 	int mapno;
2654 
2655 	if (tTd(38, 20))
2656 		printf("seq_map_close(%s)\n", map->map_mname);
2657 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2658 	{
2659 		MAP *mm = map->map_stack[mapno];
2660 
2661 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
2662 			continue;
2663 		mm->map_class->map_close(mm);
2664 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
2665 	}
2666 }
2667 
2668 
2669 /*
2670 **  SEQ_MAP_LOOKUP -- sequenced map lookup
2671 */
2672 
2673 char *
2674 seq_map_lookup(map, key, args, pstat)
2675 	MAP *map;
2676 	char *key;
2677 	char **args;
2678 	int *pstat;
2679 {
2680 	int mapno;
2681 	int mapbit = 0x01;
2682 
2683 	if (tTd(38, 20))
2684 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
2685 
2686 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
2687 	{
2688 		MAP *mm = map->map_stack[mapno];
2689 		int stat = 0;
2690 		char *rv;
2691 
2692 		if (mm == NULL)
2693 			continue;
2694 		if (!bitset(MF_OPEN, mm->map_mflags))
2695 		{
2696 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
2697 			{
2698 				*pstat = EX_UNAVAILABLE;
2699 				return NULL;
2700 			}
2701 			continue;
2702 		}
2703 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
2704 		if (rv != NULL)
2705 			return rv;
2706 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
2707 			return NULL;
2708 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
2709 		{
2710 			*pstat = stat;
2711 			return NULL;
2712 		}
2713 	}
2714 	return NULL;
2715 }
2716 
2717 
2718 /*
2719 **  SEQ_MAP_STORE -- sequenced map store
2720 */
2721 
2722 void
2723 seq_map_store(map, key, val)
2724 	MAP *map;
2725 	char *key;
2726 	char *val;
2727 {
2728 	int mapno;
2729 
2730 	if (tTd(38, 12))
2731 		printf("seq_map_store(%s, %s, %s)\n",
2732 			map->map_mname, key, val);
2733 
2734 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
2735 	{
2736 		MAP *mm = map->map_stack[mapno];
2737 
2738 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
2739 			continue;
2740 
2741 		mm->map_class->map_store(mm, key, val);
2742 		return;
2743 	}
2744 	syserr("seq_map_store(%s, %s, %s): no writable map",
2745 		map->map_mname, key, val);
2746 }
2747 /*
2748 **  GETCANONNAME -- look up name using service switch
2749 **
2750 **	Parameters:
2751 **		host -- the host name to look up.
2752 **		hbsize -- the size of the host buffer.
2753 **		trymx -- if set, try MX records.
2754 **
2755 **	Returns:
2756 **		TRUE -- if the host was found.
2757 **		FALSE -- otherwise.
2758 */
2759 
2760 bool
2761 getcanonname(host, hbsize, trymx)
2762 	char *host;
2763 	int hbsize;
2764 	bool trymx;
2765 {
2766 	int nmaps;
2767 	int mapno;
2768 	bool found = FALSE;
2769 	auto int stat;
2770 	char *maptype[MAXMAPSTACK];
2771 	short mapreturn[MAXMAPACTIONS];
2772 	extern int h_errno;
2773 
2774 	nmaps = switch_map_find("hosts", maptype, mapreturn);
2775 	for (mapno = 0; mapno < nmaps; mapno++)
2776 	{
2777 		int i;
2778 
2779 		if (tTd(38, 20))
2780 			printf("getcanonname(%s), trying %s\n",
2781 				host, maptype[mapno]);
2782 		if (strcmp("files", maptype[mapno]) == 0)
2783 			found = text_getcanonname(host, hbsize, &stat);
2784 #ifdef NIS
2785 		else if (strcmp("nis", maptype[mapno]) == 0)
2786 			found = nis_getcanonname(host, hbsize, &stat);
2787 #endif
2788 #ifdef NISPLUS
2789 		else if (strcmp("nisplus", maptype[mapno]) == 0)
2790 			found = nisplus_getcanonname(host, hbsize, &stat);
2791 #endif
2792 #if NAMED_BIND
2793 		else if (strcmp("dns", maptype[mapno]) == 0)
2794 			found = dns_getcanonname(host, hbsize, trymx, &stat);
2795 #endif
2796 		else
2797 		{
2798 			found = FALSE;
2799 			stat = EX_UNAVAILABLE;
2800 		}
2801 		if (found)
2802 			break;
2803 
2804 		/* see if we should continue */
2805 		if (stat == EX_TEMPFAIL)
2806 			i = MA_TRYAGAIN;
2807 		else if (stat == EX_NOHOST)
2808 			i = MA_NOTFOUND;
2809 		else
2810 			i = MA_UNAVAIL;
2811 		if (bitset(1 << mapno, mapreturn[i]))
2812 			break;
2813 	}
2814 
2815 	if (found)
2816 	{
2817 		char *d;
2818 
2819 		if (tTd(38, 20))
2820 			printf("getcanonname(%s), found\n", host);
2821 
2822 		/*
2823 		**  If returned name is still single token, compensate
2824 		**  by tagging on $m.  This is because some sites set
2825 		**  up their DNS or NIS databases wrong.
2826 		*/
2827 
2828 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
2829 		{
2830 			d = macvalue('m', CurEnv);
2831 			if (d != NULL &&
2832 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
2833 			{
2834 				if (host[strlen(host) - 1] != '.')
2835 					strcat(host, ".");
2836 				strcat(host, d);
2837 			}
2838 			else
2839 			{
2840 				return FALSE;
2841 			}
2842 		}
2843 		return TRUE;
2844 	}
2845 
2846 	if (tTd(38, 20))
2847 		printf("getcanonname(%s), failed, stat=%d\n", host, stat);
2848 
2849 #if NAMED_BIND
2850 	if (stat == EX_NOHOST)
2851 		h_errno = HOST_NOT_FOUND;
2852 	else
2853 		h_errno = TRY_AGAIN;
2854 #endif
2855 
2856 	return FALSE;
2857 }
2858 /*
2859 **  NULL stubs
2860 */
2861 
2862 bool
2863 null_map_open(map, mode)
2864 	MAP *map;
2865 	int mode;
2866 {
2867 	return TRUE;
2868 }
2869 
2870 void
2871 null_map_close(map)
2872 	MAP *map;
2873 {
2874 	return;
2875 }
2876 
2877 void
2878 null_map_store(map, key, val)
2879 	MAP *map;
2880 	char *key;
2881 	char *val;
2882 {
2883 	return;
2884 }
2885 
2886 
2887 /*
2888 **  BOGUS stubs
2889 */
2890 
2891 char *
2892 bogus_map_lookup(map, key, args, pstat)
2893 	MAP *map;
2894 	char *key;
2895 	char **args;
2896 	int *pstat;
2897 {
2898 	*pstat = EX_TEMPFAIL;
2899 	return NULL;
2900 }
2901 
2902 MAPCLASS	BogusMapClass =
2903 {
2904 	"bogus-map",		NULL,		0,
2905 	NULL,		bogus_map_lookup,	null_map_store,
2906 	null_map_open,	null_map_close,
2907 };
2908