xref: /original-bsd/usr.sbin/sendmail/src/map.c (revision b6592f3d)
1 /*
2  * Copyright (c) 1992 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.29 (Berkeley) 08/21/94";
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 #include <rpcsvc/ypclnt.h>
23 #endif
24 
25 /*
26 **  MAP.C -- implementations for various map classes.
27 **
28 **	Each map class implements a series of functions:
29 **
30 **	bool map_parse(MAP *map, char *args)
31 **		Parse the arguments from the config file.  Return TRUE
32 **		if they were ok, FALSE otherwise.  Fill in map with the
33 **		values.
34 **
35 **	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
36 **		Look up the key in the given map.  If found, do any
37 **		rewriting the map wants (including "args" if desired)
38 **		and return the value.  Set *pstat to the appropriate status
39 **		on error and return NULL.  Args will be NULL if called
40 **		from the alias routines, although this should probably
41 **		not be relied upon.  It is suggested you call map_rewrite
42 **		to return the results -- it takes care of null termination
43 **		and uses a dynamically expanded buffer as needed.
44 **
45 **	void map_store(MAP *map, char *key, char *value)
46 **		Store the key:value pair in the map.
47 **
48 **	bool map_open(MAP *map, int mode)
49 **		Open the map for the indicated mode.  Mode should
50 **		be either O_RDONLY or O_RDWR.  Return TRUE if it
51 **		was opened successfully, FALSE otherwise.  If the open
52 **		failed an the MF_OPTIONAL flag is not set, it should
53 **		also print an error.  If the MF_ALIAS bit is set
54 **		and this map class understands the @:@ convention, it
55 **		should call aliaswait() before returning.
56 **
57 **	void map_close(MAP *map)
58 **		Close the map.
59 */
60 
61 #define DBMMODE		0644
62 
63 extern bool	aliaswait __P((MAP *, char *, int));
64 /*
65 **  MAP_PARSEARGS -- parse config line arguments for database lookup
66 **
67 **	This is a generic version of the map_parse method.
68 **
69 **	Parameters:
70 **		map -- the map being initialized.
71 **		ap -- a pointer to the args on the config line.
72 **
73 **	Returns:
74 **		TRUE -- if everything parsed OK.
75 **		FALSE -- otherwise.
76 **
77 **	Side Effects:
78 **		null terminates the filename; stores it in map
79 */
80 
81 bool
82 map_parseargs(map, ap)
83 	MAP *map;
84 	char *ap;
85 {
86 	register char *p = ap;
87 
88 	map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
89 	for (;;)
90 	{
91 		while (isascii(*p) && isspace(*p))
92 			p++;
93 		if (*p != '-')
94 			break;
95 		switch (*++p)
96 		{
97 		  case 'N':
98 			map->map_mflags |= MF_INCLNULL;
99 			map->map_mflags &= ~MF_TRY0NULL;
100 			break;
101 
102 		  case 'O':
103 			map->map_mflags &= ~MF_TRY1NULL;
104 			break;
105 
106 		  case 'o':
107 			map->map_mflags |= MF_OPTIONAL;
108 			break;
109 
110 		  case 'f':
111 			map->map_mflags |= MF_NOFOLDCASE;
112 			break;
113 
114 		  case 'm':
115 			map->map_mflags |= MF_MATCHONLY;
116 			break;
117 
118 		  case 'a':
119 			map->map_app = ++p;
120 			break;
121 
122 		  case 'k':
123 			while (isascii(*++p) && isspace(*p))
124 				continue;
125 			map->map_keycolnm = p;
126 			break;
127 
128 		  case 'v':
129 			while (isascii(*++p) && isspace(*p))
130 				continue;
131 			map->map_valcolnm = p;
132 			break;
133 
134 		  case 'z':
135 			if (*++p != '\\')
136 				map->map_coldelim = *p;
137 			else
138 			{
139 				switch (*++p)
140 				{
141 				  case 'n':
142 					map->map_coldelim = '\n';
143 					break;
144 
145 				  case 't':
146 					map->map_coldelim = '\t';
147 					break;
148 
149 				  default:
150 					map->map_coldelim = '\\';
151 				}
152 			}
153 			break;
154 		}
155 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
156 			p++;
157 		if (*p != '\0')
158 			*p++ = '\0';
159 	}
160 	if (map->map_app != NULL)
161 		map->map_app = newstr(map->map_app);
162 	if (map->map_keycolnm != NULL)
163 		map->map_keycolnm = newstr(map->map_keycolnm);
164 	if (map->map_valcolnm != NULL)
165 		map->map_valcolnm = newstr(map->map_valcolnm);
166 
167 	if (*p != '\0')
168 	{
169 		map->map_file = p;
170 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
171 			p++;
172 		if (*p != '\0')
173 			*p++ = '\0';
174 		map->map_file = newstr(map->map_file);
175 	}
176 
177 	while (*p != '\0' && isascii(*p) && isspace(*p))
178 		p++;
179 	if (*p != '\0')
180 		map->map_rebuild = newstr(p);
181 
182 	if (map->map_file == NULL)
183 	{
184 		syserr("No file name for %s map %s",
185 			map->map_class->map_cname, map->map_mname);
186 		return FALSE;
187 	}
188 	return TRUE;
189 }
190 /*
191 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
192 **
193 **	It also adds the map_app string.  It can be used as a utility
194 **	in the map_lookup method.
195 **
196 **	Parameters:
197 **		map -- the map that causes this.
198 **		s -- the string to rewrite, NOT necessarily null terminated.
199 **		slen -- the length of s.
200 **		av -- arguments to interpolate into buf.
201 **
202 **	Returns:
203 **		Pointer to rewritten result.
204 **
205 **	Side Effects:
206 **		none.
207 */
208 
209 struct rwbuf
210 {
211 	int	rwb_len;	/* size of buffer */
212 	char	*rwb_buf;	/* ptr to buffer */
213 };
214 
215 struct rwbuf	RwBufs[2];	/* buffers for rewriting output */
216 
217 char *
218 map_rewrite(map, s, slen, av)
219 	register MAP *map;
220 	register char *s;
221 	int slen;
222 	char **av;
223 {
224 	register char *bp;
225 	register char c;
226 	char **avp;
227 	register char *ap;
228 	register struct rwbuf *rwb;
229 	int i;
230 	int len;
231 
232 	if (tTd(39, 1))
233 	{
234 		printf("map_rewrite(%.*s), av =", slen, s);
235 		if (av == NULL)
236 			printf(" (nullv)");
237 		else
238 		{
239 			for (avp = av; *avp != NULL; avp++)
240 				printf("\n\t%s", *avp);
241 		}
242 		printf("\n");
243 	}
244 
245 	rwb = RwBufs;
246 	if (av == NULL)
247 		rwb++;
248 
249 	/* count expected size of output (can safely overestimate) */
250 	i = len = slen;
251 	if (av != NULL)
252 	{
253 		bp = s;
254 		for (i = slen; --i >= 0 && (c = *bp++) != 0; )
255 		{
256 			if (c != '%')
257 				continue;
258 			if (--i < 0)
259 				break;
260 			c = *bp++;
261 			if (!(isascii(c) && isdigit(c)))
262 				continue;
263 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
264 				continue;
265 			if (*avp == NULL)
266 				continue;
267 			len += strlen(*avp);
268 		}
269 	}
270 	if (map->map_app != NULL)
271 		len += strlen(map->map_app);
272 	if (rwb->rwb_len < ++len)
273 	{
274 		/* need to malloc additional space */
275 		rwb->rwb_len = len;
276 		if (rwb->rwb_buf != NULL)
277 			free(rwb->rwb_buf);
278 		rwb->rwb_buf = xalloc(rwb->rwb_len);
279 	}
280 
281 	bp = rwb->rwb_buf;
282 	if (av == NULL)
283 	{
284 		bcopy(s, bp, slen);
285 		bp += slen;
286 	}
287 	else
288 	{
289 		while (--slen >= 0 && (c = *s++) != '\0')
290 		{
291 			if (c != '%')
292 			{
293   pushc:
294 				*bp++ = c;
295 				continue;
296 			}
297 			if (--slen < 0 || (c = *s++) == '\0')
298 				c = '%';
299 			if (c == '%')
300 				goto pushc;
301 			if (!(isascii(c) && isdigit(c)))
302 			{
303 				*bp++ = '%';
304 				goto pushc;
305 			}
306 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
307 				continue;
308 			if (*avp == NULL)
309 				continue;
310 
311 			/* transliterate argument into output string */
312 			for (ap = *avp; (c = *ap++) != '\0'; )
313 				*bp++ = c;
314 		}
315 	}
316 	if (map->map_app != NULL)
317 		strcpy(bp, map->map_app);
318 	else
319 		*bp = '\0';
320 	if (tTd(39, 1))
321 		printf("map_rewrite => %s\n", rwb->rwb_buf);
322 	return rwb->rwb_buf;
323 }
324 /*
325 **  INITMAPS -- initialize for aliasing
326 **
327 **	Parameters:
328 **		rebuild -- if TRUE, this rebuilds the cached versions.
329 **		e -- current envelope.
330 **
331 **	Returns:
332 **		none.
333 **
334 **	Side Effects:
335 **		initializes aliases:
336 **		if NDBM:  opens the database.
337 **		if ~NDBM: reads the aliases into the symbol table.
338 */
339 
340 initmaps(rebuild, e)
341 	bool rebuild;
342 	register ENVELOPE *e;
343 {
344 	extern void map_init();
345 
346 #ifdef XDEBUG
347 	checkfd012("entering initmaps");
348 #endif
349 	CurEnv = e;
350 	if (rebuild)
351 	{
352 		stabapply(map_init, 1);
353 		stabapply(map_init, 2);
354 	}
355 	else
356 	{
357 		stabapply(map_init, 0);
358 	}
359 #ifdef XDEBUG
360 	checkfd012("exiting initmaps");
361 #endif
362 }
363 
364 void
365 map_init(s, rebuild)
366 	register STAB *s;
367 	int rebuild;
368 {
369 	register MAP *map;
370 
371 	/* has to be a map */
372 	if (s->s_type != ST_MAP)
373 		return;
374 
375 	map = &s->s_map;
376 	if (!bitset(MF_VALID, map->map_mflags))
377 		return;
378 
379 	if (tTd(38, 2))
380 		printf("map_init(%s:%s, %s, %d)\n",
381 			map->map_class->map_cname == NULL ? "NULL" :
382 				map->map_class->map_cname,
383 			map->map_mname == NULL ? "NULL" : map->map_mname,
384 			map->map_file == NULL ? "NULL" : map->map_file,
385 			rebuild);
386 
387 	if (rebuild == (bitset(MF_ALIAS, map->map_mflags) &&
388 		    bitset(MCF_REBUILDABLE, map->map_class->map_cflags) ? 1 : 2))
389 	{
390 		if (tTd(38, 3))
391 			printf("\twrong pass\n");
392 		return;
393 	}
394 
395 	/* if already open, close it (for nested open) */
396 	if (bitset(MF_OPEN, map->map_mflags))
397 	{
398 		map->map_class->map_close(map);
399 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
400 	}
401 
402 	if (rebuild == 2)
403 	{
404 		rebuildaliases(map, FALSE);
405 	}
406 	else
407 	{
408 		if (map->map_class->map_open(map, O_RDONLY))
409 		{
410 			if (tTd(38, 4))
411 				printf("\t%s:%s %s: valid\n",
412 					map->map_class->map_cname == NULL ? "NULL" :
413 						map->map_class->map_cname,
414 					map->map_mname == NULL ? "NULL" :
415 						map->map_mname,
416 					map->map_file == NULL ? "NULL" :
417 						map->map_file);
418 			map->map_mflags |= MF_OPEN;
419 		}
420 		else if (tTd(38, 4))
421 			printf("\t%s:%s %s: invalid: %s\n",
422 				map->map_class->map_cname == NULL ? "NULL" :
423 					map->map_class->map_cname,
424 				map->map_mname == NULL ? "NULL" :
425 					map->map_mname,
426 				map->map_file == NULL ? "NULL" :
427 					map->map_file,
428 				errstring(errno));
429 	}
430 }
431 /*
432 **  NDBM modules
433 */
434 
435 #ifdef NDBM
436 
437 /*
438 **  DBM_MAP_OPEN -- DBM-style map open
439 */
440 
441 bool
442 ndbm_map_open(map, mode)
443 	MAP *map;
444 	int mode;
445 {
446 	register DBM *dbm;
447 	struct stat st;
448 
449 	if (tTd(38, 2))
450 		printf("ndbm_map_open(%s, %s, %d)\n",
451 			map->map_mname, map->map_file, mode);
452 
453 	if (mode == O_RDWR)
454 		mode |= O_CREAT|O_TRUNC;
455 
456 	/* open the database */
457 	dbm = dbm_open(map->map_file, mode, DBMMODE);
458 	if (dbm == NULL)
459 	{
460 #ifdef MAYBENEXTRELEASE
461 		if (aliaswait(map, ".pag", FALSE))
462 			return TRUE;
463 #endif
464 		if (!bitset(MF_OPTIONAL, map->map_mflags))
465 			syserr("Cannot open DBM database %s", map->map_file);
466 		return FALSE;
467 	}
468 	map->map_db1 = (void *) dbm;
469 	if (mode == O_RDONLY)
470 	{
471 		if (bitset(MF_ALIAS, map->map_mflags) &&
472 		    !aliaswait(map, ".pag", TRUE))
473 			return FALSE;
474 	}
475 	else
476 	{
477 		int fd;
478 
479 		/* exclusive lock for duration of rebuild */
480 		fd = dbm_dirfno((DBM *) map->map_db1);
481 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags) &&
482 		    lockfile(fd, map->map_file, ".dir", LOCK_EX))
483 			map->map_mflags |= MF_LOCKED;
484 	}
485 	if (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0)
486 		map->map_mtime = st.st_mtime;
487 	return TRUE;
488 }
489 
490 
491 /*
492 **  DBM_MAP_LOOKUP -- look up a datum in a DBM-type map
493 */
494 
495 char *
496 ndbm_map_lookup(map, name, av, statp)
497 	MAP *map;
498 	char *name;
499 	char **av;
500 	int *statp;
501 {
502 	datum key, val;
503 	int fd;
504 	char keybuf[MAXNAME + 1];
505 
506 	if (tTd(38, 20))
507 		printf("ndbm_map_lookup(%s, %s)\n",
508 			map->map_mname, name);
509 
510 	key.dptr = name;
511 	key.dsize = strlen(name);
512 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
513 	{
514 		if (key.dsize > sizeof keybuf - 1)
515 			key.dsize = sizeof keybuf - 1;
516 		bcopy(key.dptr, keybuf, key.dsize + 1);
517 		makelower(keybuf);
518 		key.dptr = keybuf;
519 	}
520 	fd = dbm_dirfno((DBM *) map->map_db1);
521 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
522 		(void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
523 	val.dptr = NULL;
524 	if (bitset(MF_TRY0NULL, map->map_mflags))
525 	{
526 		val = dbm_fetch((DBM *) map->map_db1, key);
527 		if (val.dptr != NULL)
528 			map->map_mflags &= ~MF_TRY1NULL;
529 	}
530 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
531 	{
532 		key.dsize++;
533 		val = dbm_fetch((DBM *) map->map_db1, key);
534 		if (val.dptr != NULL)
535 			map->map_mflags &= ~MF_TRY0NULL;
536 	}
537 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
538 		(void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
539 	if (val.dptr == NULL)
540 		return NULL;
541 	if (bitset(MF_MATCHONLY, map->map_mflags))
542 		return map_rewrite(map, name, strlen(name), NULL);
543 	else
544 		return map_rewrite(map, val.dptr, val.dsize, av);
545 }
546 
547 
548 /*
549 **  DBM_MAP_STORE -- store a datum in the database
550 */
551 
552 void
553 ndbm_map_store(map, lhs, rhs)
554 	register MAP *map;
555 	char *lhs;
556 	char *rhs;
557 {
558 	datum key;
559 	datum data;
560 	int stat;
561 
562 	if (tTd(38, 12))
563 		printf("ndbm_map_store(%s, %s, %s)\n",
564 			map->map_mname, lhs, rhs);
565 
566 	key.dsize = strlen(lhs);
567 	key.dptr = lhs;
568 
569 	data.dsize = strlen(rhs);
570 	data.dptr = rhs;
571 
572 	if (bitset(MF_INCLNULL, map->map_mflags))
573 	{
574 		key.dsize++;
575 		data.dsize++;
576 	}
577 
578 	stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
579 	if (stat > 0)
580 	{
581 		usrerr("050 Warning: duplicate alias name %s", lhs);
582 		stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
583 	}
584 	if (stat != 0)
585 		syserr("readaliases: dbm put (%s)", lhs);
586 }
587 
588 
589 /*
590 **  NDBM_MAP_CLOSE -- close the database
591 */
592 
593 void
594 ndbm_map_close(map)
595 	register MAP  *map;
596 {
597 	if (tTd(38, 9))
598 		printf("ndbm_map_close(%s, %s, %x)\n",
599 			map->map_mname, map->map_file, map->map_mflags);
600 
601 	if (bitset(MF_WRITABLE, map->map_mflags))
602 	{
603 #ifdef NIS
604 		bool inclnull;
605 		char buf[200];
606 
607 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
608 		map->map_mflags &= ~MF_INCLNULL;
609 
610 		(void) sprintf(buf, "%010ld", curtime());
611 		ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
612 
613 		(void) gethostname(buf, sizeof buf);
614 		ndbm_map_store(map, "YP_MASTER_NAME", buf);
615 
616 		if (inclnull)
617 			map->map_mflags |= MF_INCLNULL;
618 #endif
619 
620 		/* write out the distinguished alias */
621 		ndbm_map_store(map, "@", "@");
622 	}
623 	dbm_close((DBM *) map->map_db1);
624 }
625 
626 #endif
627 /*
628 **  NEWDB (Hash and BTree) Modules
629 */
630 
631 #ifdef NEWDB
632 
633 /*
634 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
635 **
636 **	These do rather bizarre locking.  If you can lock on open,
637 **	do that to avoid the condition of opening a database that
638 **	is being rebuilt.  If you don't, we'll try to fake it, but
639 **	there will be a race condition.  If opening for read-only,
640 **	we immediately release the lock to avoid freezing things up.
641 **	We really ought to hold the lock, but guarantee that we won't
642 **	be pokey about it.  That's hard to do.
643 */
644 
645 bool
646 bt_map_open(map, mode)
647 	MAP *map;
648 	int mode;
649 {
650 	DB *db;
651 	int i;
652 	int omode;
653 	int fd;
654 	struct stat st;
655 	char buf[MAXNAME];
656 
657 	if (tTd(38, 2))
658 		printf("bt_map_open(%s, %s, %d)\n",
659 			map->map_mname, map->map_file, mode);
660 
661 	omode = mode;
662 	if (omode == O_RDWR)
663 	{
664 		omode |= O_CREAT|O_TRUNC;
665 #if defined(O_EXLOCK) && HASFLOCK
666 		omode |= O_EXLOCK;
667 # if !OLD_NEWDB
668 	}
669 	else
670 	{
671 		omode |= O_SHLOCK;
672 # endif
673 #endif
674 	}
675 
676 	(void) strcpy(buf, map->map_file);
677 	i = strlen(buf);
678 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
679 		(void) strcat(buf, ".db");
680 	db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
681 	if (db == NULL)
682 	{
683 #ifdef MAYBENEXTRELEASE
684 		if (aliaswait(map, ".db", FALSE))
685 			return TRUE;
686 #endif
687 		if (!bitset(MF_OPTIONAL, map->map_mflags))
688 			syserr("Cannot open BTREE database %s", map->map_file);
689 		return FALSE;
690 	}
691 #if !OLD_NEWDB
692 	fd = db->fd(db);
693 # if HASFLOCK
694 #  if !defined(O_EXLOCK)
695 	if (mode == O_RDWR && fd >= 0)
696 	{
697 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
698 			map->map_mflags |= MF_LOCKED;
699 	}
700 #  else
701 	if (mode == O_RDONLY && fd >= 0)
702 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
703 	else
704 		map->map_mflags |= MF_LOCKED;
705 #  endif
706 # endif
707 #endif
708 
709 	/* try to make sure that at least the database header is on disk */
710 	if (mode == O_RDWR)
711 #if OLD_NEWDB
712 		(void) db->sync(db);
713 #else
714 		(void) db->sync(db, 0);
715 
716 	if (fd >= 0 && fstat(fd, &st) >= 0)
717 		map->map_mtime = st.st_mtime;
718 #endif
719 
720 	map->map_db2 = (void *) db;
721 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
722 		if (!aliaswait(map, ".db", TRUE))
723 			return FALSE;
724 	return TRUE;
725 }
726 
727 
728 /*
729 **  HASH_MAP_INIT -- HASH-style map initialization
730 */
731 
732 bool
733 hash_map_open(map, mode)
734 	MAP *map;
735 	int mode;
736 {
737 	DB *db;
738 	int i;
739 	int omode;
740 	int fd;
741 	struct stat st;
742 	char buf[MAXNAME];
743 
744 	if (tTd(38, 2))
745 		printf("hash_map_open(%s, %s, %d)\n",
746 			map->map_mname, map->map_file, mode);
747 
748 	omode = mode;
749 	if (omode == O_RDWR)
750 	{
751 		omode |= O_CREAT|O_TRUNC;
752 #if defined(O_EXLOCK) && HASFLOCK
753 		omode |= O_EXLOCK;
754 # if !OLD_NEWDB
755 	}
756 	else
757 	{
758 		omode |= O_SHLOCK;
759 # endif
760 #endif
761 	}
762 
763 	(void) strcpy(buf, map->map_file);
764 	i = strlen(buf);
765 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
766 		(void) strcat(buf, ".db");
767 	db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
768 	if (db == NULL)
769 	{
770 #ifdef MAYBENEXTRELEASE
771 		if (aliaswait(map, ".db", FALSE))
772 			return TRUE;
773 #endif
774 		if (!bitset(MF_OPTIONAL, map->map_mflags))
775 			syserr("Cannot open HASH database %s", map->map_file);
776 		return FALSE;
777 	}
778 #if !OLD_NEWDB
779 	fd = db->fd(db);
780 # if HASFLOCK
781 #  if !defined(O_EXLOCK)
782 	if (mode == O_RDWR && fd >= 0)
783 	{
784 		if (lockfile(fd, map->map_file, ".db", LOCK_EX))
785 			map->map_mflags |= MF_LOCKED;
786 	}
787 #  else
788 	if (mode == O_RDONLY && fd >= 0)
789 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
790 	else
791 		map->map_mflags |= MF_LOCKED;
792 #  endif
793 # endif
794 #endif
795 
796 	/* try to make sure that at least the database header is on disk */
797 	if (mode == O_RDWR)
798 #if OLD_NEWDB
799 		(void) db->sync(db);
800 #else
801 		(void) db->sync(db, 0);
802 
803 	if (fd >= 0 && fstat(fd, &st) >= 0)
804 		map->map_mtime = st.st_mtime;
805 #endif
806 
807 	map->map_db2 = (void *) db;
808 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
809 		if (!aliaswait(map, ".db", TRUE))
810 			return FALSE;
811 	return TRUE;
812 }
813 
814 
815 /*
816 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
817 */
818 
819 char *
820 db_map_lookup(map, name, av, statp)
821 	MAP *map;
822 	char *name;
823 	char **av;
824 	int *statp;
825 {
826 	DBT key, val;
827 	register DB *db = (DB *) map->map_db2;
828 	int st;
829 	int saveerrno;
830 	int fd;
831 	char keybuf[MAXNAME + 1];
832 
833 	if (tTd(38, 20))
834 		printf("db_map_lookup(%s, %s)\n",
835 			map->map_mname, name);
836 
837 	key.size = strlen(name);
838 	if (key.size > sizeof keybuf - 1)
839 		key.size = sizeof keybuf - 1;
840 	key.data = keybuf;
841 	bcopy(name, keybuf, key.size + 1);
842 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
843 		makelower(keybuf);
844 #if !OLD_NEWDB
845 	fd = db->fd(db);
846 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
847 		(void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
848 #endif
849 	st = 1;
850 	if (bitset(MF_TRY0NULL, map->map_mflags))
851 	{
852 		st = db->get(db, &key, &val, 0);
853 		if (st == 0)
854 			map->map_mflags &= ~MF_TRY1NULL;
855 	}
856 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
857 	{
858 		key.size++;
859 		st = db->get(db, &key, &val, 0);
860 		if (st == 0)
861 			map->map_mflags &= ~MF_TRY0NULL;
862 	}
863 	saveerrno = errno;
864 #if !OLD_NEWDB
865 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
866 		(void) lockfile(fd, map->map_file, ".db", LOCK_UN);
867 #endif
868 	if (st != 0)
869 	{
870 		errno = saveerrno;
871 		if (st < 0)
872 			syserr("db_map_lookup: get (%s)", name);
873 		return NULL;
874 	}
875 	if (bitset(MF_MATCHONLY, map->map_mflags))
876 		return map_rewrite(map, name, strlen(name), NULL);
877 	else
878 		return map_rewrite(map, val.data, val.size, av);
879 }
880 
881 
882 /*
883 **  DB_MAP_STORE -- store a datum in the NEWDB database
884 */
885 
886 void
887 db_map_store(map, lhs, rhs)
888 	register MAP *map;
889 	char *lhs;
890 	char *rhs;
891 {
892 	int stat;
893 	DBT key;
894 	DBT data;
895 	register DB *db = map->map_db2;
896 
897 	if (tTd(38, 20))
898 		printf("db_map_store(%s, %s, %s)\n",
899 			map->map_mname, lhs, rhs);
900 
901 	key.size = strlen(lhs);
902 	key.data = lhs;
903 
904 	data.size = strlen(rhs);
905 	data.data = rhs;
906 
907 	if (bitset(MF_INCLNULL, map->map_mflags))
908 	{
909 		key.size++;
910 		data.size++;
911 	}
912 
913 	stat = db->put(db, &key, &data, R_NOOVERWRITE);
914 	if (stat > 0)
915 	{
916 		usrerr("050 Warning: duplicate alias name %s", lhs);
917 		stat = db->put(db, &key, &data, 0);
918 	}
919 	if (stat != 0)
920 		syserr("readaliases: db put (%s)", lhs);
921 }
922 
923 
924 /*
925 **  DB_MAP_CLOSE -- add distinguished entries and close the database
926 */
927 
928 void
929 db_map_close(map)
930 	MAP *map;
931 {
932 	register DB *db = map->map_db2;
933 
934 	if (tTd(38, 9))
935 		printf("db_map_close(%s, %s, %x)\n",
936 			map->map_mname, map->map_file, map->map_mflags);
937 
938 	if (bitset(MF_WRITABLE, map->map_mflags))
939 	{
940 		/* write out the distinguished alias */
941 		db_map_store(map, "@", "@");
942 	}
943 
944 	if (db->close(db) != 0)
945 		syserr("readaliases: db close failure");
946 }
947 
948 #endif
949 /*
950 **  NIS Modules
951 */
952 
953 # ifdef NIS
954 
955 # ifndef YPERR_BUSY
956 #  define YPERR_BUSY	16
957 # endif
958 
959 /*
960 **  NIS_MAP_OPEN -- open DBM map
961 */
962 
963 bool
964 nis_map_open(map, mode)
965 	MAP *map;
966 	int mode;
967 {
968 	int yperr;
969 	register char *p;
970 	auto char *vp;
971 	auto int vsize;
972 	char *master;
973 
974 	if (tTd(38, 2))
975 		printf("nis_map_open(%s, %s)\n",
976 			map->map_mname, map->map_file);
977 
978 	if (mode != O_RDONLY)
979 	{
980 		/* issue a pseudo-error message */
981 #ifdef ENOSYS
982 		errno = ENOSYS;
983 #else
984 # ifdef EFTYPE
985 		errno = EFTYPE;
986 # else
987 		errno = ENXIO;
988 # endif
989 #endif
990 		return FALSE;
991 	}
992 
993 	p = strchr(map->map_file, '@');
994 	if (p != NULL)
995 	{
996 		*p++ = '\0';
997 		if (*p != '\0')
998 			map->map_domain = p;
999 	}
1000 
1001 	if (*map->map_file == '\0')
1002 		map->map_file = "mail.aliases";
1003 
1004 	if (map->map_domain == NULL)
1005 	{
1006 		yperr = yp_get_default_domain(&map->map_domain);
1007 		if (yperr != 0)
1008 		{
1009 			if (!bitset(MF_OPTIONAL, map->map_mflags))
1010 				syserr("NIS map %s specified, but NIS not running\n",
1011 					map->map_file);
1012 			return FALSE;
1013 		}
1014 	}
1015 
1016 	/* check to see if this map actually exists */
1017 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1018 			&vp, &vsize);
1019 	if (tTd(38, 10))
1020 		printf("nis_map_open: yp_match(%s, %s) => %s\n",
1021 			map->map_domain, map->map_file, yperr_string(yperr));
1022 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1023 		return TRUE;
1024 
1025 	if (!bitset(MF_OPTIONAL, map->map_mflags))
1026 		syserr("Cannot bind to domain %s: %s", map->map_domain,
1027 			yperr_string(yperr));
1028 
1029 	return FALSE;
1030 }
1031 
1032 
1033 /*
1034 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
1035 */
1036 
1037 char *
1038 nis_map_lookup(map, name, av, statp)
1039 	MAP *map;
1040 	char *name;
1041 	char **av;
1042 	int *statp;
1043 {
1044 	char *vp;
1045 	auto int vsize;
1046 	int buflen;
1047 	int yperr;
1048 	char keybuf[MAXNAME + 1];
1049 
1050 	if (tTd(38, 20))
1051 		printf("nis_map_lookup(%s, %s)\n",
1052 			map->map_mname, name);
1053 
1054 	buflen = strlen(name);
1055 	if (buflen > sizeof keybuf - 1)
1056 		buflen = sizeof keybuf - 1;
1057 	bcopy(name, keybuf, buflen + 1);
1058 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1059 		makelower(keybuf);
1060 	yperr = YPERR_KEY;
1061 	if (bitset(MF_TRY0NULL, map->map_mflags))
1062 	{
1063 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1064 			     &vp, &vsize);
1065 		if (yperr == 0)
1066 			map->map_mflags &= ~MF_TRY1NULL;
1067 	}
1068 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1069 	{
1070 		buflen++;
1071 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1072 			     &vp, &vsize);
1073 		if (yperr == 0)
1074 			map->map_mflags &= ~MF_TRY0NULL;
1075 	}
1076 	if (yperr != 0)
1077 	{
1078 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1079 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
1080 		return NULL;
1081 	}
1082 	if (bitset(MF_MATCHONLY, map->map_mflags))
1083 		return map_rewrite(map, name, strlen(name), NULL);
1084 	else
1085 		return map_rewrite(map, vp, vsize, av);
1086 }
1087 
1088 #endif
1089 /*
1090 **  STAB (Symbol Table) Modules
1091 */
1092 
1093 
1094 /*
1095 **  STAB_MAP_LOOKUP -- look up alias in symbol table
1096 */
1097 
1098 char *
1099 stab_map_lookup(map, name, av, pstat)
1100 	register MAP *map;
1101 	char *name;
1102 	char **av;
1103 	int *pstat;
1104 {
1105 	register STAB *s;
1106 
1107 	if (tTd(38, 20))
1108 		printf("stab_lookup(%s, %s)\n",
1109 			map->map_mname, name);
1110 
1111 	s = stab(name, ST_ALIAS, ST_FIND);
1112 	if (s != NULL)
1113 		return (s->s_alias);
1114 	return (NULL);
1115 }
1116 
1117 
1118 /*
1119 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1120 */
1121 
1122 void
1123 stab_map_store(map, lhs, rhs)
1124 	register MAP *map;
1125 	char *lhs;
1126 	char *rhs;
1127 {
1128 	register STAB *s;
1129 
1130 	s = stab(lhs, ST_ALIAS, ST_ENTER);
1131 	s->s_alias = newstr(rhs);
1132 }
1133 
1134 
1135 /*
1136 **  STAB_MAP_OPEN -- initialize (reads data file)
1137 **
1138 **	This is a wierd case -- it is only intended as a fallback for
1139 **	aliases.  For this reason, opens for write (only during a
1140 **	"newaliases") always fails, and opens for read open the
1141 **	actual underlying text file instead of the database.
1142 */
1143 
1144 bool
1145 stab_map_open(map, mode)
1146 	register MAP *map;
1147 	int mode;
1148 {
1149 	FILE *af;
1150 	struct stat st;
1151 
1152 	if (tTd(38, 2))
1153 		printf("stab_map_open(%s, %s)\n",
1154 			map->map_mname, map->map_file);
1155 
1156 	if (mode != O_RDONLY)
1157 	{
1158 		errno = ENODEV;
1159 		return FALSE;
1160 	}
1161 
1162 	af = fopen(map->map_file, "r");
1163 	if (af == NULL)
1164 		return FALSE;
1165 	readaliases(map, af, FALSE, FALSE);
1166 
1167 	if (fstat(fileno(af), &st) >= 0)
1168 		map->map_mtime = st.st_mtime;
1169 	fclose(af);
1170 
1171 	return TRUE;
1172 }
1173 /*
1174 **  Implicit Modules
1175 **
1176 **	Tries several types.  For back compatibility of aliases.
1177 */
1178 
1179 
1180 /*
1181 **  IMPL_MAP_LOOKUP -- lookup in best open database
1182 */
1183 
1184 char *
1185 impl_map_lookup(map, name, av, pstat)
1186 	MAP *map;
1187 	char *name;
1188 	char **av;
1189 	int *pstat;
1190 {
1191 	if (tTd(38, 20))
1192 		printf("impl_map_lookup(%s, %s)\n",
1193 			map->map_mname, name);
1194 
1195 #ifdef NEWDB
1196 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1197 		return db_map_lookup(map, name, av, pstat);
1198 #endif
1199 #ifdef NDBM
1200 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1201 		return ndbm_map_lookup(map, name, av, pstat);
1202 #endif
1203 	return stab_map_lookup(map, name, av, pstat);
1204 }
1205 
1206 /*
1207 **  IMPL_MAP_STORE -- store in open databases
1208 */
1209 
1210 void
1211 impl_map_store(map, lhs, rhs)
1212 	MAP *map;
1213 	char *lhs;
1214 	char *rhs;
1215 {
1216 #ifdef NEWDB
1217 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1218 		db_map_store(map, lhs, rhs);
1219 #endif
1220 #ifdef NDBM
1221 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1222 		ndbm_map_store(map, lhs, rhs);
1223 #endif
1224 	stab_map_store(map, lhs, rhs);
1225 }
1226 
1227 /*
1228 **  IMPL_MAP_OPEN -- implicit database open
1229 */
1230 
1231 bool
1232 impl_map_open(map, mode)
1233 	MAP *map;
1234 	int mode;
1235 {
1236 	struct stat stb;
1237 
1238 	if (tTd(38, 2))
1239 		printf("impl_map_open(%s, %s, %d)\n",
1240 			map->map_mname, map->map_file, mode);
1241 
1242 	if (stat(map->map_file, &stb) < 0)
1243 	{
1244 		/* no alias file at all */
1245 		if (tTd(38, 3))
1246 			printf("no map file\n");
1247 		return FALSE;
1248 	}
1249 
1250 #ifdef NEWDB
1251 	map->map_mflags |= MF_IMPL_HASH;
1252 	if (hash_map_open(map, mode))
1253 	{
1254 #if defined(NDBM) && defined(NIS)
1255 		if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
1256 #endif
1257 			return TRUE;
1258 	}
1259 	else
1260 		map->map_mflags &= ~MF_IMPL_HASH;
1261 #endif
1262 #ifdef NDBM
1263 	map->map_mflags |= MF_IMPL_NDBM;
1264 	if (ndbm_map_open(map, mode))
1265 	{
1266 		return TRUE;
1267 	}
1268 	else
1269 		map->map_mflags &= ~MF_IMPL_NDBM;
1270 #endif
1271 
1272 #if defined(NEWDB) || defined(NDBM)
1273 	if (Verbose)
1274 		message("WARNING: cannot open alias database %s", map->map_file);
1275 #else
1276 	if (mode != O_RDONLY)
1277 		usrerr("Cannot rebuild aliases: no database format defined");
1278 #endif
1279 
1280 	return stab_map_open(map, mode);
1281 }
1282 
1283 
1284 /*
1285 **  IMPL_MAP_CLOSE -- close any open database(s)
1286 */
1287 
1288 void
1289 impl_map_close(map)
1290 	MAP *map;
1291 {
1292 #ifdef NEWDB
1293 	if (bitset(MF_IMPL_HASH, map->map_mflags))
1294 	{
1295 		db_map_close(map);
1296 		map->map_mflags &= ~MF_IMPL_HASH;
1297 	}
1298 #endif
1299 
1300 #ifdef NDBM
1301 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
1302 	{
1303 		ndbm_map_close(map);
1304 		map->map_mflags &= ~MF_IMPL_NDBM;
1305 	}
1306 #endif
1307 }
1308 /*
1309 **  User map class.
1310 **
1311 **	Provides access to the system password file.
1312 */
1313 
1314 /*
1315 **  USER_MAP_OPEN -- open user map
1316 **
1317 **	Really just binds field names to field numbers.
1318 */
1319 
1320 bool
1321 user_map_open(map, mode)
1322 	MAP *map;
1323 	int mode;
1324 {
1325 	if (tTd(38, 2))
1326 		printf("user_map_open(%s)\n", map->map_mname);
1327 
1328 	if (mode != O_RDONLY)
1329 	{
1330 		/* issue a pseudo-error message */
1331 #ifdef ENOSYS
1332 		errno = ENOSYS;
1333 #else
1334 # ifdef EFTYPE
1335 		errno = EFTYPE;
1336 # else
1337 		errno = ENXIO;
1338 # endif
1339 #endif
1340 		return FALSE;
1341 	}
1342 	if (map->map_valcolnm == NULL)
1343 		/* nothing */ ;
1344 	else if (strcasecmp(map->map_valcolnm, "name") == 0)
1345 		map->map_valcolno = 1;
1346 	else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
1347 		map->map_valcolno = 2;
1348 	else if (strcasecmp(map->map_valcolnm, "uid") == 0)
1349 		map->map_valcolno = 3;
1350 	else if (strcasecmp(map->map_valcolnm, "gid") == 0)
1351 		map->map_valcolno = 4;
1352 	else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
1353 		map->map_valcolno = 5;
1354 	else if (strcasecmp(map->map_valcolnm, "dir") == 0)
1355 		map->map_valcolno = 6;
1356 	else if (strcasecmp(map->map_valcolnm, "shell") == 0)
1357 		map->map_valcolno = 7;
1358 	else
1359 	{
1360 		syserr("User map %s: unknown column name %s",
1361 			map->map_mname, map->map_valcolnm);
1362 		return FALSE;
1363 	}
1364 	return TRUE;
1365 }
1366 
1367 
1368 /*
1369 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
1370 */
1371 
1372 #include <pwd.h>
1373 
1374 char *
1375 user_map_lookup(map, key, av, statp)
1376 	MAP *map;
1377 	char *key;
1378 	char **av;
1379 	int *statp;
1380 {
1381 	struct passwd *pw;
1382 
1383 	if (tTd(38, 20))
1384 		printf("user_map_lookup(%s, %s)\n",
1385 			map->map_mname, key);
1386 
1387 	pw = getpwnam(key);
1388 	if (pw == NULL)
1389 		return NULL;
1390 	if (bitset(MF_MATCHONLY, map->map_mflags))
1391 		return map_rewrite(map, key, strlen(key), NULL);
1392 	else
1393 	{
1394 		char *rwval;
1395 		char buf[30];
1396 
1397 		switch (map->map_valcolno)
1398 		{
1399 		  case 0:
1400 		  case 1:
1401 			rwval = key;
1402 			break;
1403 
1404 		  case 2:
1405 			rwval = pw->pw_passwd;
1406 			break;
1407 
1408 		  case 3:
1409 			sprintf(buf, "%d", pw->pw_uid);
1410 			rwval = buf;
1411 			break;
1412 
1413 		  case 4:
1414 			sprintf(buf, "%d", pw->pw_gid);
1415 			rwval = buf;
1416 			break;
1417 
1418 		  case 5:
1419 			rwval = pw->pw_gecos;
1420 			break;
1421 
1422 		  case 6:
1423 			rwval = pw->pw_dir;
1424 			break;
1425 
1426 		  case 7:
1427 			rwval = pw->pw_shell;
1428 			break;
1429 		}
1430 		return map_rewrite(map, rwval, strlen(rwval), av);
1431 	}
1432 }
1433 /*
1434 **  Sequenced map type.
1435 **
1436 **	Tries each map in order until something matches, much like
1437 **	implicit.  Stores go to the first map in the list that can
1438 **	support storing.
1439 */
1440 
1441 /*
1442 **  SEQ_MAP_PARSE -- Sequenced map parsing
1443 */
1444 
1445 bool
1446 seq_map_parse(map, ap)
1447 	MAP *map;
1448 	char *ap;
1449 {
1450 	int maxmap;
1451 
1452 	if (tTd(38, 2))
1453 		printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
1454 	maxmap = 0;
1455 	while (*ap != '\0')
1456 	{
1457 		register char *p;
1458 		STAB *s;
1459 
1460 		/* find beginning of map name */
1461 		while (isascii(*ap) && isspace(*ap))
1462 			ap++;
1463 		for (p = ap; isascii(*p) && isalnum(*p); p++)
1464 			continue;
1465 		if (*p != '\0')
1466 			*p++ = '\0';
1467 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
1468 			p++;
1469 		if (*ap == '\0')
1470 		{
1471 			ap = p;
1472 			continue;
1473 		}
1474 		s = stab(ap, ST_MAP, ST_FIND);
1475 		if (s == NULL)
1476 		{
1477 			syserr("Sequence map %s: unknown member map %s",
1478 				map->map_mname, ap);
1479 		}
1480 		else if (maxmap == MAXMAPSTACK)
1481 		{
1482 			syserr("Sequence map %s: too many member maps (%d max)",
1483 				map->map_mname, MAXMAPSTACK);
1484 			maxmap++;
1485 		}
1486 		else if (maxmap < MAXMAPSTACK)
1487 		{
1488 			map->map_stack[maxmap++] = &s->s_map;
1489 		}
1490 		ap = p;
1491 	}
1492 	return TRUE;
1493 }
1494 
1495 
1496 /*
1497 **  SEQ_MAP_LOOKUP -- sequenced map lookup
1498 */
1499 
1500 char *
1501 seq_map_lookup(map, key, args, pstat)
1502 	MAP *map;
1503 	char *key;
1504 	char **args;
1505 	int *pstat;
1506 {
1507 	int mapno;
1508 	int mapbit = 0x01;
1509 
1510 	if (tTd(38, 20))
1511 		printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
1512 
1513 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
1514 	{
1515 		MAP *mm = map->map_stack[mapno];
1516 		int stat = 0;
1517 		char *rv;
1518 
1519 		if (mm == NULL)
1520 			continue;
1521 		if (!bitset(MF_OPEN, mm->map_mflags))
1522 		{
1523 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
1524 				return NULL;
1525 			continue;
1526 		}
1527 		rv = mm->map_class->map_lookup(mm, key, args, &stat);
1528 		if (rv != NULL)
1529 			return rv;
1530 		if (stat == 0 && bitset(mapbit, map->map_return[MA_NOTFOUND]))
1531 			return NULL;
1532 		if (stat != 0 && bitset(mapbit, map->map_return[MA_TRYAGAIN]))
1533 		{
1534 			*pstat = stat;
1535 			return NULL;
1536 		}
1537 	}
1538 	return NULL;
1539 }
1540 
1541 
1542 /*
1543 **  SEQ_MAP_STORE -- sequenced map store
1544 */
1545 
1546 void
1547 seq_map_store(map, key, val)
1548 	MAP *map;
1549 	char *key;
1550 	char *val;
1551 {
1552 	int mapno;
1553 
1554 	if (tTd(38, 12))
1555 		printf("seq_map_store(%s, %s, %s)\n",
1556 			map->map_mname, key, val);
1557 
1558 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
1559 	{
1560 		MAP *mm = map->map_stack[mapno];
1561 
1562 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
1563 			continue;
1564 
1565 		mm->map_class->map_store(mm, key, val);
1566 		return;
1567 	}
1568 	syserr("seq_map_store(%s, %s, %s): no writable map",
1569 		map->map_mname, key, val);
1570 }
1571 /*
1572 **  NULL stubs
1573 */
1574 
1575 bool
1576 null_map_open(map, mode)
1577 	MAP *map;
1578 	int mode;
1579 {
1580 	return TRUE;
1581 }
1582 
1583 void
1584 null_map_close(map)
1585 	MAP *map;
1586 {
1587 	return;
1588 }
1589 
1590 void
1591 null_map_store(map, key, val)
1592 	MAP *map;
1593 	char *key;
1594 	char *val;
1595 {
1596 	return;
1597 }
1598