]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/makemap/makemap.c
Merge commit '850ef5ae11d69ea3381bd310f564f025fc8caea3'
[FreeBSD/FreeBSD.git] / contrib / sendmail / makemap / makemap.c
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
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
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 *
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
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 }