xref: /freebsd/contrib/sendmail/makemap/makemap.c (revision d39bd2c1)
1 /*
2  * Copyright (c) 1998-2002, 2004, 2008, 2020 Proofpoint, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1992 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13 
14 #include <sm/gen.h>
15 
16 SM_IDSTR(copyright,
17 "@(#) Copyright (c) 1998-2002, 2004 Proofpoint, Inc. and its suppliers.\n\
18 	All rights reserved.\n\
19      Copyright (c) 1992 Eric P. Allman.  All rights reserved.\n\
20      Copyright (c) 1992, 1993\n\
21 	The Regents of the University of California.  All rights reserved.\n")
22 
23 SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.183 2013-11-22 20:51:52 ca Exp $")
24 
25 
26 #include <sys/types.h>
27 #ifndef ISC_UNIX
28 # include <sys/file.h>
29 #endif
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #ifdef EX_OK
34 # undef EX_OK		/* unistd.h may have another use for this */
35 #endif
36 #include <sysexits.h>
37 #include <sendmail/sendmail.h>
38 #include <sm/path.h>
39 #include <sendmail/pathnames.h>
40 #include <libsmdb/smdb.h>
41 #if USE_EAI
42 # include <sm/ixlen.h>
43 #endif
44 
45 uid_t	RealUid;
46 gid_t	RealGid;
47 char	*RealUserName;
48 uid_t	RunAsUid;
49 gid_t	RunAsGid;
50 char	*RunAsUserName;
51 int	Verbose = 2;
52 bool	DontInitGroups = false;
53 uid_t	TrustedUid = 0;
54 BITMAP256 DontBlameSendmail;
55 
56 static bool verbose = false;
57 static int exitstat;
58 
59 #define BUFSIZE		1024
60 #define ISASCII(c)	isascii((unsigned char)(c))
61 #define ISSPACE(c)	(ISASCII(c) && isspace(c))
62 #define ISSEP(c) (sep == '\0' ? ISASCII(c) && isspace(c) : (c) == sep)
63 
64 static void usage __P((const char *));
65 static char *readcf __P((const char *, char *, bool));
66 static void db_put __P((SMDB_DATABASE *, SMDB_DBENT, SMDB_DBENT, int, const char *, int, const char *));
67 
68 static void
usage(progname)69 usage(progname)
70 	const char *progname;
71 {
72 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
73 		      "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n",
74 		      progname);
75 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
76 		      "       %*s [-d] [-e] [-f] [-i type] [-l] [-o] [-r] [-s] [-t delimiter]\n",
77 		      (int) strlen(progname), "");
78 #if _FFR_TESTS
79 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
80 		      "       %*s [-S n]\n",
81 		      (int) strlen(progname), "");
82 #endif
83 	sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
84 		      "       %*s [-u] [-v] type mapname\n",
85 		      (int) strlen(progname), "");
86 	exit(EX_USAGE);
87 }
88 
89 /*
90 **  DB_PUT -- do the DB insert
91 **
92 **	Parameters:
93 **		database -- DB to use
94 **		db_key -- key
95 **		db_val -- value
96 **		putflags -- flags for smdb_put()
97 **		mapname -- name of map (for error reporting)
98 **		lineno -- line number (for error reporting)
99 **		progname -- name of program (for error reporting)
100 **
101 **	Returns:
102 **		none.
103 **
104 **	Side effects:
105 **		Sets exitstat so makemap exits with error if put fails
106 */
107 
108 static void
db_put(database,db_key,db_val,putflags,mapname,lineno,progname)109 db_put(database, db_key, db_val, putflags, mapname, lineno, progname)
110 	SMDB_DATABASE *database;
111 	SMDB_DBENT db_key, db_val;
112 	int putflags;
113 	const char *mapname;
114 	int lineno;
115 	const char *progname;
116 {
117 	int errcode;
118 
119 	if (verbose)
120 	{
121 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
122 				     "key=`%s', val=`%s'\n",
123 				     (char *) db_key.data,
124 				     (char *) db_val.data);
125 	}
126 
127 	errcode = database->smdb_put(database, &db_key, &db_val, putflags);
128 	if (0 == errcode)
129 		return;
130 
131 	(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s: ",
132 			     progname, mapname);
133 	if (lineno >= 0)
134 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "line %u: ",
135 				     lineno);
136 	(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "key %s: ",
137 			     (char *) db_key.data);
138 	if (SMDBE_KEY_EXIST == errcode)
139 	{
140 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
141 				     "duplicate key\n");
142 		exitstat = EX_DATAERR;
143 	}
144 	else
145 	{
146 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
147 				     "put error: %s\n", sm_errstring(errcode));
148 		exitstat = EX_IOERR;
149 	}
150 }
151 
152 /*
153 **  READCF -- read some settings from configuration file.
154 **
155 **	Parameters:
156 **		cfile -- configuration file name.
157 **		mapfile -- file name of map to look up (if not NULL/empty)
158 **			Note: this finds the first match, so in case someone
159 **			uses the same map file for different maps, they are
160 **			hopefully using the same map type.
161 **		fullpath -- compare the full paths or just the "basename"s?
162 **			(even excluding any .ext !)
163 **
164 **	Returns:
165 **		pointer to map class name (static!)
166 */
167 
168 static char *
readcf(cfile,mapfile,fullpath)169 readcf(cfile, mapfile, fullpath)
170 	const char *cfile;
171 	char *mapfile;
172 	bool fullpath;
173 {
174 	SM_FILE_T *cfp;
175 	char buf[MAXLINE];
176 	static char classbuf[MAXLINE];
177 	char *classname, *mapname;
178 	char *p;
179 
180 	if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile,
181 			      SM_IO_RDONLY, NULL)) == NULL)
182 	{
183 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
184 			      "makemap: %s: %s\n",
185 			      cfile, sm_errstring(errno));
186 		exit(EX_NOINPUT);
187 	}
188 	classname = NULL;
189 	classbuf[0] = '\0';
190 
191 	mapname = mapfile;
192 	if (!fullpath && mapfile != NULL)
193 	{
194 		p = strrchr(mapfile, '/');
195 		if (p != NULL)
196 			mapfile = ++p;
197 		mapname = strdup(mapfile);
198 		if (NULL == mapname)
199 		{
200 			sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
201 			      "makemap: strdup(%s) failed: %s\n",
202 			      mapfile, sm_errstring(errno));
203 			exit(EX_OSERR);
204 		}
205 		if ((p = strchr(mapname, '.')) != NULL)
206 			*p = '\0';
207 	}
208 
209 	while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
210 	{
211 		char *b;
212 
213 		if ((b = strchr(buf, '\n')) != NULL)
214 			*b = '\0';
215 
216 		b = buf;
217 		switch (*b++)
218 		{
219 		  case 'O':		/* option */
220 #if HASFCHOWN
221 			if (strncasecmp(b, " TrustedUser", 12) == 0 &&
222 			    !(ISASCII(b[12]) && isalnum(b[12])))
223 			{
224 				b = strchr(b, '=');
225 				if (b == NULL)
226 					continue;
227 				while (ISASCII(*++b) && isspace(*b))
228 					continue;
229 				if (ISASCII(*b) && isdigit(*b))
230 					TrustedUid = atoi(b);
231 				else
232 				{
233 					struct passwd *pw;
234 
235 					TrustedUid = 0;
236 					pw = getpwnam(b);
237 					if (pw == NULL)
238 						(void) sm_io_fprintf(smioerr,
239 								     SM_TIME_DEFAULT,
240 								     "TrustedUser: unknown user %s\n", b);
241 					else
242 						TrustedUid = pw->pw_uid;
243 				}
244 
245 # ifdef UID_MAX
246 				if (TrustedUid > UID_MAX)
247 				{
248 					(void) sm_io_fprintf(smioerr,
249 							     SM_TIME_DEFAULT,
250 							     "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
251 						(long) TrustedUid,
252 						(long) UID_MAX);
253 					TrustedUid = 0;
254 				}
255 # endif /* UID_MAX */
256 			}
257 #endif /* HASFCHOWN */
258 			break;
259 
260 		  case 'K':		/* Keyfile (map) */
261 			if (classname != NULL)	/* found it already */
262 				continue;
263 			if (mapname == NULL || *mapname == '\0')
264 				continue;
265 
266 			/* cut off trailing spaces */
267 			for (p = buf + strlen(buf) - 1;
268 			     ISASCII(*p) && isspace(*p) && p > buf; p--)
269 				*p = '\0';
270 
271 			/* find the last argument */
272 			p = strrchr(buf, ' ');
273 			if (p == NULL)
274 				continue;
275 			b = strstr(p, mapname);
276 			if (b == NULL)
277 				continue;
278 			if (b <= buf)
279 				continue;
280 			if (!fullpath)
281 			{
282 				p = strrchr(b, '.');
283 				if (p != NULL)
284 					*p = '\0';
285 			}
286 
287 			/* allow trailing white space? */
288 			if (strcmp(mapname, b) != 0)
289 				continue;
290 			/* SM_ASSERT(b > buf); */
291 			--b;
292 			if (!ISASCII(*b))
293 				continue;
294 			if (!isspace(*b) && fullpath)
295 				continue;
296 			if (!fullpath && !(SM_IS_DIR_DELIM(*b) || isspace(*b)))
297 				continue;
298 
299 			/* basically from readcf.c */
300 			for (b = buf + 1; ISASCII(*b) && isspace(*b); b++)
301 				;
302 			if (!(ISASCII(*b) && isalnum(*b)))
303 			{
304 				/* syserr("readcf: config K line: no map name"); */
305 				return NULL;
306 			}
307 
308 			while ((ISASCII(*++b) && isalnum(*b)) || *b == '_' || *b == '.')
309 				;
310 			if (*b != '\0')
311 				*b++ = '\0';
312 			while (ISASCII(*b) && isspace(*b))
313 				b++;
314 			if (!(ISASCII(*b) && isalnum(*b)))
315 			{
316 				/* syserr("readcf: config K line, map %s: no map class", b); */
317 				return NULL;
318 			}
319 			classname = b;
320 			while (ISASCII(*++b) && isalnum(*b))
321 				;
322 			if (*b != '\0')
323 				*b++ = '\0';
324 			(void) sm_strlcpy(classbuf, classname, sizeof classbuf);
325 			break;
326 
327 		  default:
328 			continue;
329 		}
330 	}
331 	(void) sm_io_close(cfp, SM_TIME_DEFAULT);
332 
333 	/* not really needed because it is just a "one time leak" */
334 	if (mapname != mapfile && mapname != NULL)
335 	{
336 		free(mapname);
337 		mapname = NULL;
338 	}
339 	return classbuf;
340 }
341 
342 int
main(argc,argv)343 main(argc, argv)
344 	int argc;
345 	char **argv;
346 {
347 	char *progname;
348 	char *cfile;
349 	bool inclnull = false;
350 	bool notrunc = false;
351 	bool allowreplace = false;
352 	bool allowempty = false;
353 	bool foldcase = true;
354 	bool unmake = false;
355 #if _FFR_MM_ALIASES
356 	/*
357 	**  NOTE: this does not work properly:
358 	**  sendmail does address rewriting which is not done here.
359 	*/
360 
361 	bool aliases = false;
362 #endif
363 	bool didreadcf = false;
364 	char sep = '\0';
365 	char comment = '#';
366 	int opt;
367 	char *typename = NULL;
368 	char *fallback = NULL;
369 	char *mapname = NULL;
370 	unsigned int lineno;
371 	int mode;
372 	int smode;
373 	int putflags = 0;
374 	long sff = SFF_ROOTOK|SFF_REGONLY;
375 	struct passwd *pw;
376 	SMDB_DATABASE *database;
377 	SMDB_CURSOR *cursor;
378 	SMDB_DBENT db_key, db_val;
379 	SMDB_DBPARAMS params;
380 	SMDB_USER_INFO user_info;
381 	char ibuf[BUFSIZE];
382 	static char rnamebuf[MAXNAME];	/* holds RealUserName */
383 	extern char *optarg;
384 	extern int optind;
385 #if USE_EAI
386 	bool ascii = true;
387 #endif
388 #if _FFR_TESTS
389 	int slp = 0;
390 #endif
391 
392 	memset(&params, '\0', sizeof params);
393 	params.smdbp_cache_size = 1024 * 1024;
394 
395 	progname = strrchr(argv[0], '/');
396 	if (progname != NULL)
397 		progname++;
398 	else
399 		progname = argv[0];
400 	cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
401 
402 	clrbitmap(DontBlameSendmail);
403 	RunAsUid = RealUid = getuid();
404 	RunAsGid = RealGid = getgid();
405 	pw = getpwuid(RealUid);
406 	if (pw != NULL)
407 		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
408 	else
409 		(void) sm_snprintf(rnamebuf, sizeof rnamebuf,
410 		    "Unknown UID %d", (int) RealUid);
411 	RunAsUserName = RealUserName = rnamebuf;
412 	user_info.smdbu_id = RunAsUid;
413 	user_info.smdbu_group_id = RunAsGid;
414 	(void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
415 		       SMDB_MAX_USER_NAME_LEN);
416 
417 #define OPTIONS		"C:D:Nc:defi:Llorst:uvx"
418 #if _FFR_MM_ALIASES
419 # define A_OPTIONS		"a"
420 #else
421 # define A_OPTIONS
422 #endif
423 #if _FFR_TESTS
424 # define X_OPTIONS		"S:"
425 #else
426 # define X_OPTIONS
427 #endif
428 	while ((opt = getopt(argc, argv, A_OPTIONS OPTIONS X_OPTIONS)) != -1)
429 	{
430 		switch (opt)
431 		{
432 		  case 'C':
433 			cfile = optarg;
434 			break;
435 
436 		  case 'N':
437 			inclnull = true;
438 			break;
439 
440 #if _FFR_MM_ALIASES
441 		  case 'a':
442 			/* Note: this doesn't verify e-mail addresses */
443 			sep = ':';
444 			aliases = true;
445 			break;
446 #endif
447 
448 		  case 'c':
449 			params.smdbp_cache_size = atol(optarg);
450 			break;
451 
452 		  case 'd':
453 			params.smdbp_allow_dup = true;
454 			break;
455 
456 		  case 'e':
457 			allowempty = true;
458 			break;
459 
460 		  case 'f':
461 			foldcase = false;
462 			break;
463 
464 		  case 'i':
465 			fallback =optarg;
466 			break;
467 
468 		  case 'D':
469 			comment = *optarg;
470 			break;
471 
472 		  case 'L':
473 			smdb_print_available_types(false);
474 			sm_io_fprintf(smioout, SM_TIME_DEFAULT,
475 				      "cf\nCF\n");
476 			exit(EX_OK);
477 			break;
478 
479 		  case 'l':
480 			smdb_print_available_types(false);
481 			exit(EX_OK);
482 			break;
483 
484 		  case 'o':
485 			notrunc = true;
486 			break;
487 
488 		  case 'r':
489 			allowreplace = true;
490 			break;
491 
492 #if _FFR_TESTS
493 		  case 'S':
494 			slp = atoi(optarg);
495 			break;
496 #endif
497 
498 		  case 's':
499 			setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
500 			setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
501 			setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail);
502 			setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail);
503 			break;
504 
505 		  case 't':
506 			if (optarg == NULL || *optarg == '\0')
507 			{
508 				sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
509 					      "Invalid separator\n");
510 				break;
511 			}
512 			sep = *optarg;
513 			break;
514 
515 		  case 'u':
516 			unmake = true;
517 			break;
518 
519 		  case 'v':
520 			verbose = true;
521 			break;
522 
523 		  case 'x':
524 			smdb_print_available_types(true);
525 			exit(EX_OK);
526 			break;
527 
528 		  default:
529 			usage(progname);
530 			/* NOTREACHED */
531 		}
532 	}
533 
534 	if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
535 		sff |= SFF_NOSLINK;
536 	if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
537 		sff |= SFF_NOHLINK;
538 	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
539 		sff |= SFF_NOWLINK;
540 
541 	argc -= optind;
542 	argv += optind;
543 	if (argc != 2)
544 	{
545 		usage(progname);
546 		/* NOTREACHED */
547 	}
548 	else
549 	{
550 		typename = argv[0];
551 		mapname = argv[1];
552 	}
553 
554 #define TYPEFROMCF	(strcasecmp(typename, "cf") == 0)
555 #define FULLPATHFROMCF	(strcmp(typename, "cf") == 0)
556 
557 #if HASFCHOWN
558 	if (geteuid() == 0)
559 	{
560 		if (TYPEFROMCF)
561 			typename = readcf(cfile, mapname, FULLPATHFROMCF);
562 		else
563 			(void) readcf(cfile, NULL, false);
564 		didreadcf = true;
565 	}
566 #endif /* HASFCHOWN */
567 
568 	if (!params.smdbp_allow_dup && !allowreplace)
569 		putflags = SMDBF_NO_OVERWRITE;
570 
571 	if (unmake)
572 	{
573 		mode = O_RDONLY;
574 		smode = S_IRUSR;
575 	}
576 	else
577 	{
578 		mode = O_RDWR;
579 		if (!notrunc)
580 		{
581 			mode |= O_CREAT|O_TRUNC;
582 			sff |= SFF_CREAT;
583 		}
584 		smode = S_IWUSR;
585 	}
586 
587 	params.smdbp_num_elements = 4096;
588 
589 	if (!didreadcf && TYPEFROMCF)
590 	{
591 		typename = readcf(cfile, mapname, FULLPATHFROMCF);
592 		didreadcf = true;
593 	}
594 	if (didreadcf && (typename == NULL || *typename == '\0'))
595 	{
596 		if (fallback != NULL && *fallback != '\0')
597 		{
598 			typename = fallback;
599 			if (verbose)
600 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
601 				     "%s: mapfile %s: not found in %s, using fallback %s\n",
602 				     progname, mapname, cfile, fallback);
603 		}
604 		else
605 		{
606 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
607 				     "%s: mapfile %s: not found in %s\n",
608 				     progname, mapname, cfile);
609 			exit(EX_DATAERR);
610 		}
611 	}
612 
613 	/*
614 	**  Note: if "implicit" is selected it does not work like
615 	**  sendmail: it will just use the first available DB type,
616 	**  it won't try several (for -u) to find one that "works".
617 	*/
618 
619 	errno = smdb_open_database(&database, mapname, mode, smode, sff,
620 				   typename, &user_info, &params);
621 	if (errno != SMDBE_OK)
622 	{
623 		char *hint;
624 
625 		if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
626 		    (hint = smdb_db_definition(typename)) != NULL)
627 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
628 					     "%s: Need to recompile with -D%s for %s support\n",
629 					     progname, hint, typename);
630 		else
631 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
632 					     "%s: error opening type %s map %s: %s\n",
633 					     progname, typename, mapname,
634 					     sm_errstring(errno));
635 		exit(EX_CANTCREAT);
636 	}
637 
638 	(void) database->smdb_sync(database, 0);
639 
640 	if (!unmake && geteuid() == 0 && TrustedUid != 0)
641 	{
642 		errno = database->smdb_set_owner(database, TrustedUid, -1);
643 		if (errno != SMDBE_OK)
644 		{
645 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
646 					     "WARNING: ownership change on %s failed %s",
647 					     mapname, sm_errstring(errno));
648 		}
649 	}
650 
651 	/*
652 	**  Copy the data
653 	*/
654 
655 	exitstat = EX_OK;
656 	if (unmake)
657 	{
658 		errno = database->smdb_cursor(database, &cursor, 0);
659 		if (errno != SMDBE_OK)
660 		{
661 
662 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
663 					     "%s: cannot make cursor for type %s map %s\n",
664 					     progname, typename, mapname);
665 			exit(EX_SOFTWARE);
666 		}
667 
668 		memset(&db_key, '\0', sizeof db_key);
669 		memset(&db_val, '\0', sizeof db_val);
670 
671 		for (lineno = 0; ; lineno++)
672 		{
673 			errno = cursor->smdbc_get(cursor, &db_key, &db_val,
674 						  SMDB_CURSOR_GET_NEXT);
675 			if (errno != SMDBE_OK)
676 				break;
677 
678 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
679 					     "%.*s%c%.*s\n",
680 					     (int) db_key.size,
681 					     (char *) db_key.data,
682 					     (sep != '\0') ? sep : '\t',
683 					     (int) db_val.size,
684 					     (char *)db_val.data);
685 
686 		}
687 		(void) cursor->smdbc_close(cursor);
688 	}
689 	else
690 	{
691 		lineno = 0;
692 		while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
693 		       >= 0)
694 		{
695 			register char *p;
696 
697 			lineno++;
698 
699 			/*
700 			**  Parse the line.
701 			*/
702 
703 			p = strchr(ibuf, '\n');
704 			if (p != NULL)
705 				*p = '\0';
706 			else if (!sm_io_eof(smioin))
707 			{
708 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
709 						     "%s: %s: line %u: line too long (%ld bytes max)\n",
710 						     progname, mapname, lineno,
711 						     (long) sizeof ibuf);
712 				exitstat = EX_DATAERR;
713 				continue;
714 			}
715 
716 			if (ibuf[0] == '\0' || ibuf[0] == comment)
717 				continue;
718 			if (sep == '\0' && ISASCII(ibuf[0]) && isspace(ibuf[0]))
719 			{
720 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
721 						     "%s: %s: line %u: syntax error (leading space)\n",
722 						     progname, mapname, lineno);
723 				exitstat = EX_DATAERR;
724 				continue;
725 			}
726 
727 			memset(&db_key, '\0', sizeof db_key);
728 			memset(&db_val, '\0', sizeof db_val);
729 			db_key.data = ibuf;
730 
731 #if USE_EAI
732 			db_key.size = 0;
733 			if (foldcase)
734 			{
735 				for (p = ibuf; *p != '\0' && !ISSEP(*p); p++)
736 				{
737 					if (!ISASCII(*p))
738 						ascii = false;
739 				}
740 				if (!ascii)
741 				{
742 					char sep;
743 					char *lkey;
744 
745 					sep = *p;
746 					*p = '\0';
747 
748 					lkey = sm_lowercase(ibuf);
749 					db_key.data = lkey;
750 					db_key.size = strlen(lkey);
751 					*p = sep;
752 				}
753 			}
754 			if (ascii)
755 #endif /* USE_EAI */
756 			/* NOTE: see if () above! */
757 			for (p = ibuf; *p != '\0' && !ISSEP(*p); p++)
758 			{
759 				if (foldcase && ISASCII(*p) && isupper(*p))
760 					*p = tolower(*p);
761 			}
762 #if USE_EAI
763 			if (0 == db_key.size)
764 #endif
765 				db_key.size = p - ibuf;
766 			if (inclnull)
767 				db_key.size++;
768 
769 			if (*p != '\0')
770 				*p++ = '\0';
771 			while (*p != '\0' && ISSEP(*p))
772 				p++;
773 #if _FFR_MM_ALIASES
774 			while (aliases && *p != '\0' && ISSPACE(*p))
775 				p++;
776 #endif
777 			if (!allowempty && *p == '\0')
778 			{
779 				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
780 						     "%s: %s: line %u: no RHS for LHS %s\n",
781 						     progname, mapname, lineno,
782 						     (char *) db_key.data);
783 				exitstat = EX_DATAERR;
784 				continue;
785 			}
786 
787 			db_val.data = p;
788 			db_val.size = strlen(p);
789 			if (inclnull)
790 				db_val.size++;
791 
792 			/*
793 			**  Do the database insert.
794 			*/
795 
796 			db_put(database, db_key, db_val, putflags, mapname,
797 				lineno, progname);
798 		}
799 #if _FFR_MM_ALIASES
800 		if (aliases)
801 		{
802 			char magic[2] = "@";
803 
804 			db_key.data = magic;
805 			db_val.data = magic;
806 			db_key.size = 1;
807 			db_val.size = 1;
808 			db_put(database, db_key, db_val, putflags, mapname, -1,
809 				progname);
810 		}
811 #endif /* _FFR_MM_ALIASES */
812 	}
813 
814 #if _FFR_TESTS
815 	if (slp > 0)
816 		sleep(slp);
817 #endif
818 
819 	/*
820 	**  Now close the database.
821 	*/
822 
823 	errno = database->smdb_close(database);
824 	if (errno != SMDBE_OK)
825 	{
826 		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
827 				     "%s: close(%s): %s\n",
828 				     progname, mapname, sm_errstring(errno));
829 		exitstat = EX_IOERR;
830 	}
831 	smdb_free_database(database);
832 
833 	exit(exitstat);
834 
835 	/* NOTREACHED */
836 	return exitstat;
837 }
838