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