Fix build on systems where size_t is (non-long) uint
[exim.git] / src / src / buildconfig.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2016 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8
9 /*************************************************
10 *       Build configuration header for Exim      *
11 *************************************************/
12
13 /* This auxiliary program builds the file config.h by the following
14 process:
15
16 First, it determines the size of off_t and time_t variables, and generates
17 macro code to define OFF_T_FMT and TIME_T_FMT as suitable formats, if they are
18 not already defined in the system-specific header file.
19
20 Then it reads Makefile, looking for certain OS-specific definitions which it
21 uses to define some specific macros. Finally, it reads the defaults file
22 config.h.defaults.
23
24 The defaults file contains normal C #define statements for various macros; if
25 the name of a macro is found in the environment, the environment value replaces
26 the default. If the default #define does not contain any value, then that macro
27 is not copied to the created file unless there is some value in the
28 environment.
29
30 This program is compiled and run as part of the Make process and is not
31 normally called independently. */
32
33
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <pwd.h>
40 #include <grp.h>
41
42 typedef struct {
43   const char *name;
44   int *flag;
45 } have_item;
46
47 typedef struct {
48   const char *name;
49   char *data;
50 } save_item;
51
52 static const char *db_opts[] = { "", "USE_DB", "USE_GDBM", "USE_TDB" };
53
54 static int have_ipv6 = 0;
55 static int have_iconv = 0;
56
57 static char errno_quota[256];
58 static char ostype[256];
59 static char cc[256];
60
61 /* If any entry is an initial substring of another, the longer one must
62 appear first. */
63
64 static have_item have_list[] = {
65   { "HAVE_IPV6",      &have_ipv6 },
66   { "HAVE_ICONV",     &have_iconv },
67   { NULL, NULL}
68 };
69
70 static save_item save_list[] = {
71   { "ERRNO_QUOTA",    errno_quota },
72   { "OSTYPE",         ostype },
73   { "CC",             cc },
74   { NULL, NULL}
75 };
76
77
78 /* Subroutine to check a string for precisely one instance of "%s". If not,
79 bomb out. */
80
81 void
82 check_percent_ess(char *value, char *name)
83 {
84 int OK = 0;
85 char *p = strstr(value, "%s");
86 if (p != NULL) OK = strstr(p+2, "%s") == NULL;
87 if (!OK)
88   {
89   printf("\n*** \"%s\" (%s) must contain precisely one occurrence of\n"
90     "*** \"%%s\". Please review your build-time configuration.\n\n/", value,
91     name);
92   exit(1);
93   }
94 }
95
96
97 /* Main program */
98
99 int
100 main(int argc, char **argv)
101 {
102 off_t test_off_t = 0;
103 time_t test_time_t = 0;
104 #if ! (__STDC_VERSION__ >= 199901L)
105 size_t test_size_t = 0;
106 ssize_t test_ssize_t = 0;
107 unsigned long test_ulong_t = 0L;
108 unsigned int test_uint_t = 0;
109 #endif
110 long test_long_t = 0;
111 int test_int_t = 0;
112 FILE *base;
113 FILE *new;
114 int last_initial = 'A';
115 int linecount = 0;
116 int have_auth = 0;
117 int in_local_makefile = 0;
118 int use_which_db = 0;
119 int use_which_db_in_local_makefile = 0;
120 int support_crypteq = 0;
121 char buffer[1024];
122
123 if (argc != 1)
124   {
125   printf("*** Buildconfig: called with incorrect arguments\n");
126   exit(1);
127   }
128
129 new = fopen("config.h", "wb");
130 if (new == NULL)
131   {
132   printf("*** Buildconfig: failed to open config.h for output\n");
133   exit(1);
134   }
135
136 printf("Building configuration file config.h\n");
137
138 fprintf(new, "/*************************************************\n");
139 fprintf(new, "*           Configuration header for Exim        *\n");
140 fprintf(new, "*************************************************/\n\n");
141
142 fprintf(new, "/* This file was automatically generated from Makefile and "
143   "config.h.defaults,\n");
144 fprintf(new, "using values specified in the configuration file Local/Makefile.\n");
145 fprintf(new, "Do not edit it. Instead, edit Local/Makefile and "
146   "rerun make. */\n\n");
147
148 /* First, deal with the printing format for off_t variables. We assume that if
149 the size of off_t is greater than 4, "%lld" will be available as a format for
150 printing long long variables, and there will be support for the long long type.
151 This assumption is known to be OK for the common operating systems. */
152
153 fprintf(new, "#ifndef OFF_T_FMT\n");
154 if (sizeof(test_off_t) > sizeof(test_long_t))
155   {
156   fprintf(new, "#define OFF_T_FMT  \"%%lld\"\n");
157   fprintf(new, "#define LONGLONG_T long long int\n");
158   }
159 else
160   {
161   fprintf(new, "#define OFF_T_FMT  \"%%ld\"\n");
162   fprintf(new, "#define LONGLONG_T long int\n");
163   }
164 fprintf(new, "#endif\n\n");
165
166 /* Now do the same thing for time_t variables. If the length is greater than
167 4, we want to assume long long support (even if off_t was less than 4). If the
168 length is 4 or less, we can leave LONGLONG_T to whatever was defined above for
169 off_t. */
170
171 fprintf(new, "#ifndef TIME_T_FMT\n");
172 if (sizeof(test_time_t) > sizeof(test_long_t))
173   {
174   fprintf(new, "#define TIME_T_FMT  \"%%lld\"\n");
175   fprintf(new, "#undef  LONGLONG_T\n");
176   fprintf(new, "#define LONGLONG_T long long int\n");
177   }
178 else
179   {
180   fprintf(new, "#define TIME_T_FMT  \"%%ld\"\n");
181   }
182 fprintf(new, "#endif\n\n");
183
184 /* And for sizeof() results, size_t, which should with C99 be just %zu, deal
185 with C99 not being ubiquitous yet.  Unfortunately.  Assume ssize_t is same
186 size as size_t on C99; if someone comes up with a version where it's not, fix
187 it then. */
188
189 #if __STDC_VERSION__ >= 199901L
190 fprintf(new, "#define SIZE_T_FMT  \"%%zu\"\n");
191 fprintf(new, "#define SSIZE_T_FMT  \"%%zd\"\n");
192 #else
193 if (sizeof(test_size_t) > sizeof (test_ulong_t))
194   fprintf(new, "#define SIZE_T_FMT  \"%%llu\"\n");
195 else if (sizeof(test_size_t) > sizeof (test_uint_t))
196   fprintf(new, "#define SIZE_T_FMT  \"%%lu\"\n");
197 else
198   fprintf(new, "#define SIZE_T_FMT  \"%%u\"\n");
199
200 if (sizeof(test_ssize_t) > sizeof(test_long_t))
201   fprintf(new, "#define SSIZE_T_FMT  \"%%lld\"\n");
202 else if (sizeof(test_ssize_t) > sizeof(test_int_t))
203   fprintf(new, "#define SSIZE_T_FMT  \"%%ld\"\n");
204 else
205   fprintf(new, "#define SSIZE_T_FMT  \"%%d\"\n");
206 #endif
207
208 /* Now search the makefile for certain settings */
209
210 base = fopen("Makefile", "rb");
211 if (base == NULL)
212   {
213   printf("*** Buildconfig: failed to open Makefile\n");
214   (void)fclose(new);
215   exit(1);
216   }
217
218 errno_quota[0] = 0;    /* no over-riding value set */
219 ostype[0] = 0;         /* just in case */
220 cc[0] = 0;
221
222 while (fgets(buffer, sizeof(buffer), base) != NULL)
223   {
224   int i;
225   have_item *h;
226   save_item *s;
227   char *p = buffer + (int)strlen(buffer);
228   linecount++;
229   while (p > buffer && isspace((unsigned char)p[-1])) p--;
230   *p = 0;
231   p = buffer;
232   while (isspace((unsigned char)*p)) p++;
233
234   /* Notice when we hit the user's makefile */
235
236   if (strcmp(p, "# From Local/Makefile") == 0)
237     {
238     in_local_makefile = 1;
239     continue;
240     }
241
242   /* Remember the last DB option setting. If we hit two in the user's
243   Makefile, complain. */
244
245   for (i = 1; i < sizeof(db_opts)/sizeof(char *); i++)
246     {
247     int len = (int)strlen(db_opts[i]);
248     if (strncmp(p, db_opts[i], len) == 0 && (p[len] == ' ' || p[len] == '='))
249       {
250       if (in_local_makefile)
251         {
252         if (use_which_db_in_local_makefile)
253           {
254           printf("*** Only one of USE_DB, USE_GDBM, or USE_TDB should be "
255             "defined in Local/Makefile\n");
256           exit(1);
257           }
258         use_which_db_in_local_makefile = 1;
259         }
260       use_which_db = i;
261       break;
262       }
263     }
264   if (i < sizeof(db_opts)/sizeof(char *)) continue;
265
266   /* Items where we just save a boolean */
267
268   for (h = have_list; h->name != NULL; h++)
269     {
270     int len = (int)strlen(h->name);
271     if (strncmp(p, h->name, len) == 0)
272       {
273       p += len;
274       while (isspace((unsigned char)*p)) p++;
275       if (*p++ != '=')
276         {
277         printf("*** Buildconfig: syntax error in Makefile line %d\n", linecount);
278         exit(1);
279         }
280       while (isspace((unsigned char)*p)) p++;
281       if (strcmp(p, "YES") == 0 || strcmp(p, "yes") == 0) *(h->flag) = 1;
282         else *(h->flag) = 0;   /* Must reset in case multiple instances */
283       break;
284       }
285     }
286
287   if (h->name != NULL) continue;
288
289   /* Items where we save the complete string */
290
291   for (s = save_list; s->name != NULL; s++)
292     {
293     int len = (int)strlen(s->name);
294     if (strncmp(p, s->name, len) == 0)
295       {
296       p += len;
297       while (isspace((unsigned char)*p)) p++;
298       if (*p++ != '=')
299         {
300         printf("*** Buildconfig: syntax error in Makefile line %d\n", linecount);
301         exit(1);
302         }
303       while (isspace((unsigned char)*p)) p++;
304       strcpy(s->data, p);
305       }
306     }
307   }
308
309 fprintf(new, "#define HAVE_IPV6             %s\n",
310   have_ipv6? "TRUE" : "FALSE");
311
312 fprintf(new, "#define HAVE_ICONV            %s\n",
313   have_iconv? "TRUE" : "FALSE");
314
315 if (errno_quota[0] != 0)
316   fprintf(new, "\n#define ERRNO_QUOTA           %s\n", errno_quota);
317
318 if (strcmp(cc, "gcc") == 0 &&
319     (strstr(ostype, "IRIX") != NULL || strstr(ostype, "AIX") != NULL))
320   {
321   fprintf(new, "\n/* This switch includes the code to fix the inet_ntoa() */");
322   fprintf(new, "\n/* bug when using gcc on an IRIX or AIX system. */");
323   fprintf(new, "\n#define USE_INET_NTOA_FIX");
324   }
325
326 fprintf(new, "\n");
327 (void)fclose(base);
328
329
330 /* Now handle the macros listed in the defaults */
331
332 base = fopen("../src/config.h.defaults", "rb");
333 if (base == NULL)
334   {
335   printf("*** Buildconfig: failed to open ../src/config.h.defaults\n");
336   (void)fclose(new);
337   exit(1);
338   }
339
340 while (fgets(buffer, sizeof(buffer), base) != NULL)
341   {
342   int i;
343   char name[256];
344   char *value;
345   char *p = buffer;
346   char *q = name;
347
348   while (*p == ' ' || *p == '\t') p++;
349
350   if (strncmp(p, "#ifdef ", 7) == 0
351    || strncmp(p, "#ifndef ", 8) == 0
352    || strncmp(p, "#if ", 4) == 0
353    || strncmp(p, "#endif", 6) == 0
354      )
355     {
356     fputs(buffer, new);
357     continue;
358     }
359
360   if (strncmp(p, "#define ", 8) != 0) continue;
361
362   p += 8;
363   while (*p == ' ' || *p == '\t') p++;
364
365   if (*p < last_initial) fprintf(new, "\n");
366   last_initial = *p;
367
368   while (*p && (isalnum((unsigned char)*p) || *p == '_')) *q++ = *p++;
369   *q = 0;
370
371   /* USE_DB, USE_GDBM, and USE_TDB are special cases. We want to have only
372   one of them set. The scan of the Makefile has saved which was the last one
373   encountered. */
374
375   for (i = 1; i < sizeof(db_opts)/sizeof(char *); i++)
376     {
377     if (strcmp(name, db_opts[i]) == 0)
378       {
379       if (use_which_db == i)
380         fprintf(new, "#define %s %.*syes\n", db_opts[i],
381           21 - (int)strlen(db_opts[i]), "                         ");
382       else
383         fprintf(new, "/* %s not set */\n", name);
384       break;
385       }
386     }
387   if (i < sizeof(db_opts)/sizeof(char *)) continue;
388
389   /* EXIM_USER is a special case. We look in the environment for EXIM_USER or
390   EXIM_UID (the latter for backward compatibility with Exim 3). If the value is
391   not numeric, we look up the user, and default the GID if found. Otherwise,
392   EXIM_GROUP or EXIM_GID must be in the environment. */
393
394   if (strcmp(name, "EXIM_UID") == 0)
395     {
396     uid_t uid = 0;
397     gid_t gid = 0;
398     int gid_set = 0;
399     int uid_not_set = 0;
400     char *username = NULL;
401     char *groupname = NULL;
402     char *s;
403     char *user = getenv("EXIM_USER");
404     char *group = getenv("EXIM_GROUP");
405
406     if (user == NULL) user = getenv("EXIM_UID");
407     if (group == NULL) group = getenv("EXIM_GID");
408
409     if (user == NULL)
410       {
411       printf("\n*** EXIM_USER has not been defined in any of the Makefiles in "
412         "the\n    \"Local\" directory. Please review your build-time "
413         "configuration.\n\n");
414       return 1;
415       }
416
417     while (isspace((unsigned char)(*user))) user++;
418     if (*user == 0)
419       {
420       printf("\n*** EXIM_USER is defined as an empty string in one of the "
421         "files\n    in the \"Local\" directory. Please review your build-time"
422         "\n    configuration.\n\n");
423       return 1;
424       }
425
426     for (s = user; *s != 0; s++)
427       {
428       if (iscntrl((unsigned char)(*s)))
429         {
430         printf("\n*** EXIM_USER contains the control character 0x%02X in one "
431           "of the files\n    in the \"Local\" directory. Please review your "
432           "build-time\n    configuration.\n\n", *s);
433         return 1;
434         }
435       }
436
437     /* Numeric uid given */
438
439     if (user[strspn(user, "0123456789")] == 0)
440       {
441       uid = (uid_t)atoi(user);
442       }
443
444     /* User name given. Normally, we look up the uid right away. However,
445     people building binary distributions sometimes want to retain the name till
446     runtime. This is supported if the name begins "ref:". */
447
448     else if (strncmp(user, "ref:", 4) == 0)
449       {
450       user += 4;
451       while (isspace(*user)) user++;
452       username = user;
453       gid_set = 1;
454       uid_not_set = 1;
455       }
456
457     else
458       {
459       struct passwd *pw = getpwnam(user);
460       if (pw == NULL)
461         {
462         printf("\n*** User \"%s\" (specified in one of the Makefiles) does not "
463           "exist.\n    Please review your build-time configuration.\n\n",
464           user);
465         return 1;
466         }
467
468       uid = pw->pw_uid;
469       gid = pw->pw_gid;
470       gid_set = 1;
471       }
472
473     /* Use explicit group if set. */
474
475     if (group != NULL)
476       {
477       while (isspace((unsigned char)(*group))) group++;
478       if (*group == 0)
479         {
480         printf("\n*** EXIM_GROUP is defined as an empty string in one of "
481           "the files in the\n    \"Local\" directory. ");
482         if (gid_set)
483           {
484           printf("If you want the Exim group to be taken from the\n    "
485             "password data for the Exim user, just remove the EXIM_GROUP "
486             "setting.\n    Otherwise, p");
487           }
488         else printf("EXIM_USER is defined numerically, so there is no"
489           "\n    default for EXIM_GROUP and you must set it explicitly.\n    P");
490         printf("lease review your build-time configuration.\n\n");
491         return 1;
492         }
493
494       for (s = group; *s != 0; s++)
495         {
496         if (iscntrl((unsigned char)(*s)))
497           {
498           printf("\n*** EXIM_GROUP contains the control character 0x%02X in one "
499             "of the files\n    in the \"Local\" directory. Please review your "
500             "build-time\n    configuration.\n\n", *s);
501           return 1;
502           }
503         }
504
505       /* Group name given. This may be by reference or to be looked up now,
506       as for user. */
507
508       if (strncmp(group, "ref:", 4) == 0)
509         {
510         group += 4;
511         while (isspace(*group)) group++;
512         groupname = group;
513         }
514
515       else if (username != NULL)
516         {
517         groupname = group;
518         }
519
520       else if (group[strspn(group, "0123456789")] == 0)
521         {
522         gid = (gid_t)atoi(group);
523         }
524
525       else
526         {
527         struct group *gr = getgrnam(group);
528         if (gr == NULL)
529           {
530           printf("\n*** Group \"%s\" (specified in one of the Makefiles) does "
531             "not exist.\n   Please review your build-time configuration.\n\n",
532             group);
533           return 1;
534           }
535         gid = gr->gr_gid;
536         }
537       }
538
539     /* Else trouble unless found in passwd file with user */
540
541     else if (!gid_set)
542       {
543       printf("\n*** No group set for Exim. Please review your build-time "
544         "configuration.\n\n");
545       return 1;
546       }
547
548     /* security sanity checks
549     if ref: is being used, we can never be sure, but we can take reasonable
550     steps to filter out the most obvious ones.  */
551
552     if ((!uid_not_set && uid == 0) ||
553         ((username != NULL) && (
554           (strcmp(username, "root") == 0) ||
555           (strcmp(username, "toor") == 0) )))
556       {
557       printf("\n*** Exim's internal user must not be root.\n\n");
558       return 1;
559       }
560
561     /* Output user and group names or uid/gid. When names are set, uid/gid
562     are set to zero but will be replaced at runtime. */
563
564     if (username != NULL)
565       fprintf(new, "#define EXIM_USERNAME         \"%s\"\n", username);
566     if (groupname != NULL)
567       fprintf(new, "#define EXIM_GROUPNAME        \"%s\"\n", groupname);
568
569     fprintf(new, "#define EXIM_UID              %d\n", (int)uid);
570     fprintf(new, "#define EXIM_GID              %d\n", (int)gid);
571     continue;
572     }
573
574   /* CONFIGURE_OWNER and CONFIGURE_GROUP are special cases. We look in the
575   environment for first. If the value is not numeric, we look up the user or
576   group. A lot of this code is similar to that for EXIM_USER, but it's easier
577   to keep it separate. */
578
579   if (strcmp(name, "CONFIGURE_OWNER") == 0 ||
580       strcmp(name, "CONFIGURE_GROUP") == 0)
581     {
582     int isgroup = name[10] == 'G';
583     uid_t uid = 0;
584     gid_t gid = 0;
585     const char *s;
586     const char *username = NULL;
587     const char *user = getenv(name);
588
589     if (user == NULL) user = "";
590     while (isspace((unsigned char)(*user))) user++;
591     if (*user == 0)
592       {
593       fprintf(new, "/* %s not set */\n", name);
594       continue;
595       }
596
597     for (s = user; *s != 0; s++)
598       {
599       if (iscntrl((unsigned char)(*s)))
600         {
601         printf("\n*** %s contains the control character 0x%02X in "
602           "one of the files\n    in the \"Local\" directory. Please review "
603           "your build-time\n    configuration.\n\n", name, *s);
604         return 1;
605         }
606       }
607
608     /* Numeric uid given */
609
610     if (user[strspn(user, "0123456789")] == 0)
611       {
612       if (isgroup)
613         gid = (gid_t)atoi(user);
614       else
615         uid = (uid_t)atoi(user);
616       }
617
618     /* Name given. Normally, we look up the uid or gid right away. However,
619     people building binary distributions sometimes want to retain the name till
620     runtime. This is supported if the name begins "ref:". */
621
622     else if (strncmp(user, "ref:", 4) == 0)
623       {
624       user += 4;
625       while (isspace(*user)) user++;
626       username = user;
627       }
628 else if (isgroup)
629       {
630       struct group *gr = getgrnam(user);
631       if (gr == NULL)
632         {
633         printf("\n*** Group \"%s\" (specified in one of the Makefiles) does not "
634           "exist.\n    Please review your build-time configuration.\n\n",
635           user);
636         return 1;
637         }
638       gid = gr->gr_gid;
639       }
640
641     else
642       {
643       struct passwd *pw = getpwnam(user);
644       if (pw == NULL)
645         {
646         printf("\n*** User \"%s\" (specified in one of the Makefiles) does not "
647           "exist.\n    Please review your build-time configuration.\n\n",
648           user);
649         return 1;
650         }
651       uid = pw->pw_uid;
652       }
653
654     /* Output user and group names or uid/gid. When names are set, uid/gid
655     are set to zero but will be replaced at runtime. */
656
657     if (username != NULL)
658       {
659       if (isgroup)
660         fprintf(new, "#define CONFIGURE_GROUPNAME         \"%s\"\n", username);
661       else
662         fprintf(new, "#define CONFIGURE_OWNERNAME         \"%s\"\n", username);
663       }
664
665     if (isgroup)
666       fprintf(new, "#define CONFIGURE_GROUP              %d\n", (int)gid);
667     else
668       fprintf(new, "#define CONFIGURE_OWNER              %d\n", (int)uid);
669     continue;
670     }
671
672   /* FIXED_NEVER_USERS is another special case. Look up the uid values and
673   create suitable initialization data for a vector. */
674
675   if (strcmp(name, "FIXED_NEVER_USERS") == 0)
676     {
677     char *list = getenv("FIXED_NEVER_USERS");
678     if (list == NULL)
679       {
680       fprintf(new, "#define FIXED_NEVER_USERS     0\n");
681       }
682     else
683       {
684       int count = 1;
685       int i, j;
686       uid_t *vector;
687       char *p = list;
688       while (*p != 0) if (*p++ == ':') count++;
689
690       vector = malloc((count+1) * sizeof(uid_t));
691       vector[0] = (uid_t)count;
692
693       for (i = 1, j = 0; i <= count; list++, i++)
694         {
695         char name[64];
696
697         p = list;
698         while (*list != 0 && *list != ':') list++;
699         strncpy(name, p, list-p);
700         name[list-p] = 0;
701
702         if (name[0] == 0)
703           {
704           continue;
705           }
706         else if (name[strspn(name, "0123456789")] == 0)
707           {
708           vector[j++] = (uid_t)atoi(name);
709           }
710         else
711           {
712           struct passwd *pw = getpwnam(name);
713           if (pw == NULL)
714             {
715             printf("\n*** User \"%s\" (specified for FIXED_NEVER_USERS in one of the Makefiles) does not "
716               "exist.\n    Please review your build-time configuration.\n\n",
717               name);
718             return 1;
719             }
720           vector[j++] = pw->pw_uid;
721           }
722         }
723       fprintf(new, "#define FIXED_NEVER_USERS     %d", j);
724       for (i = 0; i < j; i++) fprintf(new, ", %d", (unsigned int)vector[i]);
725       fprintf(new, "\n");
726       }
727     continue;
728     }
729
730   /* WITH_CONTENT_SCAN is another special case: it must be set if either it or
731   WITH_OLD_DEMIME is set. */
732
733   if (strcmp(name, "WITH_CONTENT_SCAN") == 0)
734     {
735     char *wcs = getenv("WITH_CONTENT_SCAN");
736     char *wod = getenv("WITH_OLD_DEMIME");
737     char *dcc = getenv("EXPERIMENTAL_DCC");
738     if (wcs != NULL || wod != NULL || dcc != NULL)
739       fprintf(new, "#define WITH_CONTENT_SCAN     yes\n");
740     else fprintf(new, "/* WITH_CONTENT_SCAN not set */\n");
741     continue;
742     }
743
744   /* DISABLE_DKIM is special; must be forced if no SUPPORT_TLS */
745   if (strcmp(name, "DISABLE_DKIM") == 0)
746     {
747     char *d_dkim = getenv("DISABLE_DKIM");
748     char *tls = getenv("SUPPORT_TLS");
749
750     if (d_dkim)
751       fprintf(new, "#define DISABLE_DKIM          yes\n");
752     else if (!tls)
753       fprintf(new, "#define DISABLE_DKIM          yes /* forced by lack of TLS */\n");
754     else
755       fprintf(new, "/* DISABLE_DKIM not set */\n");
756     continue;
757     }
758
759   /* Otherwise, check whether a value exists in the environment. Remember if
760   it is an AUTH setting or SUPPORT_CRYPTEQ. */
761
762   if ((value = getenv(name)) != NULL)
763     {
764     int len;
765     len = 21 - (int)strlen(name);
766
767     if (strncmp(name, "AUTH_", 5) == 0) have_auth = 1;
768     if (strncmp(name, "SUPPORT_CRYPTEQ", 15) == 0) support_crypteq = 1;
769
770     /* The text value of LDAP_LIB_TYPE refers to a macro that gets set. */
771
772     if (strcmp(name, "LDAP_LIB_TYPE") == 0)
773       {
774       if (strcmp(value, "NETSCAPE") == 0 ||
775           strcmp(value, "UMICHIGAN") == 0 ||
776           strcmp(value, "OPENLDAP1") == 0 ||
777           strcmp(value, "OPENLDAP2") == 0 ||
778           strcmp(value, "SOLARIS") == 0 ||
779           strcmp(value, "SOLARIS7") == 0)              /* Compatibility */
780         {
781         fprintf(new, "#define LDAP_LIB_%s\n", value);
782         }
783       else
784         {
785         printf("\n*** LDAP_LIB_TYPE=%s is not a recognized LDAP library type."
786           "\n*** Please review your build-time configuration.\n\n", value);
787         return 1;
788         }
789       }
790
791     else if (strcmp(name, "RADIUS_LIB_TYPE") == 0)
792       {
793       if (strcmp(value, "RADIUSCLIENT") == 0 ||
794           strcmp(value, "RADIUSCLIENTNEW") == 0 ||
795           strcmp(value, "RADLIB") == 0)
796         {
797         fprintf(new, "#define RADIUS_LIB_%s\n", value);
798         }
799       else
800         {
801         printf("\n*** RADIUS_LIB_TYPE=%s is not a recognized RADIUS library type."
802           "\n*** Please review your build-time configuration.\n\n", value);
803         return 1;
804         }
805       }
806
807     /* Other macros get set to the environment value. */
808
809     else
810       {
811       fprintf(new, "#define %s ", name);
812       while(len-- > 0) fputc(' ', new);
813
814       /* LOG_FILE_PATH is now messy because it can be a path containing %s or
815       it can be "syslog" or ":syslog" or "syslog:path" or even "path:syslog". */
816
817       if (strcmp(name, "LOG_FILE_PATH") == 0)
818         {
819         char *ss = value;
820         for(;;)
821           {
822           char *pp;
823           char *sss = strchr(ss, ':');
824           if (sss != NULL)
825             {
826             strncpy(buffer, ss, sss-ss);
827             buffer[sss-ss] = 0;  /* For empty case */
828             }
829           else strcpy(buffer, ss);
830           pp = buffer + (int)strlen(buffer);
831           while (pp > buffer && isspace((unsigned char)pp[-1])) pp--;
832           *pp = 0;
833           if (buffer[0] != 0 && strcmp(buffer, "syslog") != 0)
834             check_percent_ess(buffer, name);
835           if (sss == NULL) break;
836           ss = sss + 1;
837           while (isspace((unsigned char)*ss)) ss++;
838           }
839         fprintf(new, "\"%s\"\n", value);
840         }
841
842       /* Timezone values HEADERS_CHARSET, TCP_WRAPPERS_DAEMON_NAME and
843       WHITELIST_D_MACROS get quoted */
844
845       else if (strcmp(name, "TIMEZONE_DEFAULT") == 0||
846                strcmp(name, "TCP_WRAPPERS_DAEMON_NAME") == 0||
847                strcmp(name, "HEADERS_CHARSET") == 0||
848                strcmp(name, "WHITELIST_D_MACROS") == 0)
849         fprintf(new, "\"%s\"\n", value);
850
851       /* GnuTLS constants; first is for debugging, others are tuning */
852
853       /* less than 0 is not-active; 0-9 are normal, API suggests higher
854       taken without problems */
855       else if (strcmp(name, "EXIM_GNUTLS_LIBRARY_LOG_LEVEL") == 0)
856         {
857         long nv;
858         char *end;
859         nv = strtol(value, &end, 10);
860         if (end != value && *end == '\0' && nv >= -1 && nv <= 100)
861           {
862           fprintf(new, "%s\n", value);
863           }
864         else
865           {
866           printf("Value of %s should be -1..9\n", name);
867           return 1;
868           }
869         }
870
871       /* how many bits Exim, as a client, demands must be in D-H */
872       /* 1024 is a historical figure; some sites actually use lower, so we
873       permit the value to be lowered "dangerously" low, but not "insanely"
874       low.  Though actually, 1024 is becoming "dangerous". */
875       else if ((strcmp(name, "EXIM_CLIENT_DH_MIN_MIN_BITS") == 0) ||
876                (strcmp(name, "EXIM_CLIENT_DH_DEFAULT_MIN_BITS") == 0) ||
877                (strcmp(name, "EXIM_SERVER_DH_BITS_PRE2_12") == 0))
878         {
879         long nv;
880         char *end;
881         nv = strtol(value, &end, 10);
882         if (end != value && *end == '\0' && nv >= 512 && nv < 500000)
883           {
884           fprintf(new, "%s\n", value);
885           }
886         else
887           {
888           printf("Unreasonable value (%s) of \"%s\".\n", value, name);
889           return 1;
890           }
891         }
892
893       /* For others, quote any paths and don't quote anything else */
894
895       else
896         {
897         if (value[0] == '/') fprintf(new, "\"%s\"\n", value);
898           else fprintf(new, "%s\n", value);
899         }
900       }
901     }
902
903   /* Value not defined in the environment; use the default */
904
905   else
906     {
907     char *t = p;
908     while (*p == ' ' || *p == '\t') p++;
909     if (*p != '\n') fputs(buffer, new); else
910       {
911       *t = 0;
912       if (strcmp(name, "BIN_DIRECTORY")   == 0 ||
913           strcmp(name, "CONFIGURE_FILE")  == 0)
914         {
915         printf("\n*** %s has not been defined in any of the Makefiles in the\n"
916           "    \"Local\" directory. "
917           "Please review your build-time configuration.\n\n", name);
918         return 1;
919         }
920
921       if (strcmp(name, "TIMEZONE_DEFAULT") == 0)
922         {
923         char *tz = getenv("TZ");
924         fprintf(new, "#define TIMEZONE_DEFAULT      ");
925         if (tz == NULL) fprintf(new, "NULL\n"); else
926           fprintf(new, "\"%s\"\n", tz);
927         }
928
929       else fprintf(new, "/* %s not set */\n", name);
930       }
931     }
932   }
933
934 (void)fclose(base);
935
936 /* If any AUTH macros were defined, ensure that SUPPORT_CRYPTEQ is also
937 defined. */
938
939 if (have_auth)
940   {
941   if (!support_crypteq) fprintf(new, "/* Force SUPPORT_CRYPTEQ for AUTH */\n"
942     "#define SUPPORT_CRYPTEQ\n");
943   }
944
945 /* End off */
946
947 fprintf(new, "\n/* End of config.h */\n");
948 (void)fclose(new);
949 return 0;
950 }
951
952 /* End of buildconfig.c */