Fix imake segfault on OpenIndiana
[oweals/cde.git] / cde / config / imake / imake.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: imake.c /main/104 1998/03/24 12:45:15 kaleb $ */
24
25 /***************************************************************************
26  *                                                                         *
27  *                                Porting Note                             *
28  *                                                                         *
29  * Add the value of BOOTSTRAPCFLAGS to the cpp_argv table so that it will  *
30  * be passed to the template file.                                         *
31  *                                                                         *
32  ***************************************************************************/
33
34 /*
35  * 
36 Copyright (c) 1985, 1986, 1987, 1998 The Open Group  
37
38 All Rights Reserved.
39
40 The above copyright notice and this permission notice shall be included in
41 all copies or substantial portions of the Software.
42
43 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
46 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
47 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
48 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49
50 Except as contained in this notice, the name of The Open Group shall not be
51 used in advertising or otherwise to promote the sale, use or other dealings
52 in this Software without prior written authorization from The Open Group.
53  * 
54  * Original Author:
55  *      Todd Brunhoff
56  *      Tektronix, inc.
57  *      While a guest engineer at Project Athena, MIT
58  *
59  * imake: the include-make program.
60  *
61  * Usage: imake [-Idir] [-Ddefine] [-T template] [-f imakefile ] [-C Imakefile.c ] [-s] [-e] [-v] [make flags]
62  *
63  * Imake takes a template file (Imake.tmpl) and a prototype (Imakefile)
64  * and runs cpp on them producing a Makefile.  It then optionally runs make
65  * on the Makefile.
66  * Options:
67  *              -D      define.  Same as cpp -D argument.
68  *              -I      Include directory.  Same as cpp -I argument.
69  *              -T      template.  Designate a template other
70  *                      than Imake.tmpl
71  *              -f      specify the Imakefile file
72  *              -C      specify the name to use instead of Imakefile.c
73  *              -s[F]   show.  Show the produced makefile on the standard
74  *                      output.  Make is not run is this case.  If a file
75  *                      argument is provided, the output is placed there.
76  *              -e[F]   execute instead of show; optionally name Makefile F
77  *              -v      verbose.  Show the make command line executed.
78  *
79  * Environment variables:
80  *              
81  *              IMAKEINCLUDE    Include directory to use in addition to "."
82  *              IMAKECPP        Cpp to use instead of /lib/cpp
83  *              IMAKEMAKE       make program to use other than what is
84  *                              found by searching the $PATH variable.
85  * Other features:
86  *      imake reads the entire cpp output into memory and then scans it
87  *      for occurences of "@@".  If it encounters them, it replaces it with
88  *      a newline.  It also trims any trailing white space on output lines
89  *      (because make gets upset at them).  This helps when cpp expands
90  *      multi-line macros but you want them to appear on multiple lines.
91  *      It also changes occurences of "XCOMM" to "#", to avoid problems
92  *      with treating commands as invalid preprocessor commands.
93  *
94  *      The macros MAKEFILE and MAKE are provided as macros
95  *      to make.  MAKEFILE is set to imake's makefile (not the constructed,
96  *      preprocessed one) and MAKE is set to argv[0], i.e. the name of
97  *      the imake program.
98  *
99  * Theory of operation:
100  *   1. Determine the name of the imakefile from the command line (-f)
101  *      or from the content of the current directory (Imakefile or imakefile).
102  *      Call this <imakefile>.  This gets added to the arguments for
103  *      make as MAKEFILE=<imakefile>.
104  *   2. Determine the name of the template from the command line (-T)
105  *      or the default, Imake.tmpl.  Call this <template>
106  *   3. Determine the name of the imakeCfile from the command line (-C)
107  *      or the default, Imakefile.c.  Call this <imakeCfile>
108  *   4. Store lines of input into <imakeCfile>:
109  *      - A c-style comment header (see ImakefileCHeader below), used
110  *        to recognize temporary files generated by imake.
111  *      - If DEFAULT_OS_NAME is defined, format the utsname struct and
112  *        call the result <defaultOsName>.  Add:
113  *              #define DefaultOSName <defaultOsName>
114  *      - If DEFAULT_OS_MAJOR_REV is defined, format the utsname struct
115  *        and call the result <defaultOsMajorVersion>.  Add:
116  *              #define DefaultOSMajorVersion <defaultOsMajorVersion>
117  *      - If DEFAULT_OS_MINOR_REV is defined, format the utsname struct
118  *        and call the result <defaultOsMinorVersion>.  Add:
119  *              #define DefaultOSMinorVersion <defaultOsMinorVersion>
120  *      - If DEFAULT_OS_TEENY_REV is defined, format the utsname struct
121  *        and call the result <defaultOsTeenyVersion>.  Add:
122  *              #define DefaultOSTeenyVersion <defaultOsTeenyVersion>
123  *      - If the file "localdefines" is readable in the current
124  *        directory, print a warning message to stderr and add: 
125  *              #define IMAKE_LOCAL_DEFINES     "localdefines"
126  *              #include IMAKE_LOCAL_DEFINES
127  *      - If the file "admindefines" is readable in the current
128  *        directory, print a warning message to stderr and add: 
129  *              #define IMAKE_ADMIN_DEFINES     "admindefines"
130  *              #include IMAKE_ADMIN_DEFINES
131  *      - The following lines:
132  *              #define INCLUDE_IMAKEFILE       < <imakefile> >
133  *              #define IMAKE_TEMPLATE          " <template> "
134  *              #include IMAKE_TEMPLATE
135  *      - If the file "adminmacros" is readable in the current
136  *        directory, print a warning message to stderr and add: 
137  *              #define IMAKE_ADMIN_MACROS      "adminmacros"
138  *              #include IMAKE_ADMIN_MACROS
139  *      - If the file "localmacros" is readable in the current
140  *        directory, print a warning message to stderr and add: 
141  *              #define IMAKE_LOCAL_MACROS      "localmacros"
142  *              #include IMAKE_LOCAL_MACROS
143  *   5. Start up cpp and provide it with this file.
144  *      Note that the define for INCLUDE_IMAKEFILE is intended for
145  *      use in the template file.  This implies that the imake is
146  *      useless unless the template file contains at least the line
147  *              #include INCLUDE_IMAKEFILE
148  *   6. Gather the output from cpp, and clean it up, expanding @@ to
149  *      newlines, stripping trailing white space, cpp control lines,
150  *      and extra blank lines, and changing XCOMM to #.  This cleaned
151  *      output is placed in a new file, default "Makefile", but can
152  *      be specified with -s or -e options.
153  *   7. Optionally start up make on the resulting file.
154  *
155  * The design of the template makefile should therefore be:
156  *      <set global macros like CFLAGS, etc.>
157  *      <include machine dependent additions>
158  *      #include INCLUDE_IMAKEFILE
159  *      <add any global targets like 'clean' and long dependencies>
160  */
161 #include <stdio.h>
162 #include <ctype.h>
163 #include "Xosdefs.h"
164 #ifdef WIN32
165 # include "Xw32defs.h"
166 #endif
167 #ifndef X_NOT_POSIX
168 # ifndef _POSIX_SOURCE
169 #  define _POSIX_SOURCE
170 # endif
171 #endif
172 #include <sys/types.h>
173 #include <fcntl.h>
174 #ifdef X_NOT_POSIX
175 # ifndef WIN32
176 #  include <sys/file.h>
177 # endif
178 #else
179 # include <unistd.h>
180 #endif
181 #if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
182 # include <signal.h>
183 #else
184 # define _POSIX_SOURCE
185 # include <signal.h>
186 # undef _POSIX_SOURCE
187 #endif
188 #include <sys/stat.h>
189 #ifndef X_NOT_POSIX
190 # ifdef _POSIX_SOURCE
191 #  include <sys/wait.h>
192 # else
193 #  define _POSIX_SOURCE
194 #  include <sys/wait.h>
195 #  undef _POSIX_SOURCE
196 # endif
197 # define waitCode(w)    WEXITSTATUS(w)
198 # define waitSig(w)     WTERMSIG(w)
199 typedef int             waitType;
200 #else /* X_NOT_POSIX */
201 # ifdef SYSV
202 #  define waitCode(w)   (((w) >> 8) & 0x7f)
203 #  define waitSig(w)    ((w) & 0xff)
204 typedef int             waitType;
205 # else /* SYSV */
206 #  ifdef WIN32
207 #   include <process.h>
208 typedef int             waitType;
209 #  else
210 #   include <sys/wait.h>
211 #   define waitCode(w)  ((w).w_T.w_Retcode)
212 #   define waitSig(w)   ((w).w_T.w_Termsig)
213 typedef union wait      waitType;
214 #  endif
215 # endif
216 # ifndef WIFSIGNALED
217 #  define WIFSIGNALED(w) waitSig(w)
218 # endif
219 # ifndef WIFEXITED
220 #  define WIFEXITED(w) waitCode(w)
221 # endif
222 #endif /* X_NOT_POSIX */
223 #ifndef X_NOT_STDC_ENV
224 # include <stdlib.h>
225 #else
226 char *malloc(), *realloc();
227 void exit();
228 #endif
229 #if defined(macII) && !defined(__STDC__)  /* stdlib.h fails to define these */
230 char *malloc(), *realloc();
231 #endif /* macII */
232 #ifdef X_NOT_STDC_ENV
233 extern char     *getenv();
234 #endif
235 #include <errno.h>
236 #ifdef X_NOT_STDC_ENV
237 extern int      errno;
238 #endif
239 #ifndef WIN32
240 #include <sys/utsname.h>
241 #else
242 #include <windows.h>
243 #endif
244 #ifndef SYS_NMLN
245 # ifdef _SYS_NMLN
246 #  define SYS_NMLN _SYS_NMLN
247 # else
248 #  define SYS_NMLN 257
249 # endif
250 #endif
251 #ifdef linux
252 #include <limits.h>
253 #endif
254 /* 
255  * is strstr() in <strings.h> on X_NOT_STDC_ENV? 
256  * are there any X_NOT_STDC_ENV machines left in the world?
257  */
258 #include <string.h>
259 #include <stdarg.h>
260 #include "imakemdep.h"
261
262 /*
263  * This define of strerror is copied from (and should be identical to)
264  * Xos.h, which we don't want to include here for bootstrapping reasons.
265  */
266 #if defined(X_NOT_STDC_ENV) || (defined(sun) && !defined(SVR4)) || defined(macII)
267 # ifndef strerror
268 extern char *sys_errlist[];
269 extern int sys_nerr;
270 #  define strerror(n) \
271     (((n) >= 0 && (n) < sys_nerr) ? sys_errlist[n] : "unknown error")
272 # endif
273 #endif
274
275 #define TRUE            1
276 #define FALSE           0
277
278 #ifdef FIXUP_CPP_WHITESPACE
279 static int      InRule = FALSE;
280 # ifdef INLINE_SYNTAX
281 static int      InInline = 0;
282 # endif
283 #endif
284 #ifdef MAGIC_MAKE_VARS
285 static int xvariable = 0;
286 static int xvariables[10];
287 #endif
288
289 /*
290  * Some versions of cpp reduce all tabs in macro expansion to a single
291  * space.  In addition, the escaped newline may be replaced with a
292  * space instead of being deleted.  Blech.
293  */
294 #ifdef FIXUP_CPP_WHITESPACE
295 static void KludgeOutputLine(char **pline);
296 static void KludgeResetRule(void);
297 #else
298 # define KludgeOutputLine(arg)
299 # define KludgeResetRule()
300 #endif
301
302 typedef unsigned char   boolean;
303
304 #ifdef USE_CC_E
305 # ifndef DEFAULT_CC
306 #  define DEFAULT_CC "cc"
307 # endif
308 #else
309 # ifndef DEFAULT_CPP
310 #  ifdef CPP_PROGRAM
311 #   define DEFAULT_CPP CPP_PROGRAM
312 #  else
313 #   define DEFAULT_CPP "/lib/cpp"
314 #  endif
315 # endif
316 #endif
317
318 static char *cpp = NULL;
319
320 static char *tmpMakefile = "/tmp/Imf.XXXXXX";
321 static char     *tmpImakefile    = "/tmp/IIf.XXXXXX";
322 static char     *make_argv[ ARGUMENTS ] = {
323 #ifdef WIN32
324     "nmake"
325 #else
326     "make"
327 #endif
328 };
329
330 static int      make_argindex;
331 static int      cpp_argindex;
332 static char     *Imakefile = NULL;
333 static char     *Makefile = "Makefile";
334 static char     *Template = "Imake.tmpl";
335 static char     *ImakefileC = "Imakefile.c";
336 static boolean haveImakefileC = FALSE;
337 static char     *cleanedImakefile = NULL;
338 static char     *program;
339 static boolean  verbose = FALSE;
340 static boolean  show = TRUE;
341
342 static char     *FindImakefile(char *);
343 static char     *ReadLine(FILE *, const char *);
344 static char     *CleanCppInput(char *);
345 static char     *Strdup(const char *);
346 static char     *Emalloc(int);
347 static void     LogFatal(const char *, ...);
348 static void     LogMsg(const char *, ...);
349 static void     Log(const char *, va_list);
350
351 static void     showit(FILE *);
352 static void     wrapup(void);
353 static
354 #ifdef SIGNALRETURNSINT
355 int
356 #else
357 void
358 #endif
359 catch(int);
360 static void     init(void);
361 static void     AddMakeArg(char *);
362 static void     AddCppArg(char *);
363 static void     SetOpts(int, char **);
364 static void     showargs(char **);
365 static void     CheckImakefileC(const char *);
366 static boolean  optional_include(FILE *, const char *, const char *);
367 static void     doit(FILE *, const char *, char **);
368 #if (defined(DEFAULT_OS_NAME) || defined(DEFAULT_OS_MAJOR_REV) || \
369      defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
370 static void     parse_utsname(struct utsname *, const char *, char *, const char *);
371 #endif
372 #if (defined(DEFAULT_OS_MAJOR_REV) || defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
373 static const char       *trim_version(const char *);
374 #endif
375 #ifdef linux
376 static void     get_distrib(FILE *);
377 static void     get_libc_version(FILE *);
378 static void     get_ld_version(FILE *);
379 #endif
380 #if defined(sun) && defined(__SVR4)
381 static void     get_sun_compiler_versions(FILE *);
382 #endif
383 static void     get_gcc_incdir(FILE *);
384 static boolean  define_os_defaults(FILE *);
385 static void     cppit(const char *i, const char *, const char *, FILE *, const char *);
386 static void     makeit(void);
387 static void     CleanCppOutput(FILE *, const char *);
388 static boolean  isempty(char *);
389 static void     writetmpfile(FILE *, const char *, int, const char *);
390
391 int
392 main(int argc, char *argv[])
393 {
394         FILE    *tmpfd;
395         char    makeMacro[ BUFSIZ ];
396         char    makefileMacro[ BUFSIZ ];
397
398         program = argv[0];
399         init();
400         SetOpts(argc, argv);
401
402         Imakefile = FindImakefile(Imakefile);
403         CheckImakefileC(ImakefileC);
404         if (Makefile)
405                 tmpMakefile = Makefile;
406         else {
407                 tmpMakefile = Strdup(tmpMakefile);
408                 int ret = mkstemp(tmpMakefile);
409                 (void) ret;
410         }
411         AddMakeArg("-f");
412         AddMakeArg( tmpMakefile );
413         snprintf(makeMacro, BUFSIZ, "MAKE=%s", program);
414         AddMakeArg( makeMacro );
415         snprintf(makefileMacro, BUFSIZ, "MAKEFILE=%s", Imakefile);
416         AddMakeArg( makefileMacro );
417
418         if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
419                 LogFatal("Cannot create temporary file %s.", tmpMakefile);
420
421         cleanedImakefile = CleanCppInput(Imakefile);
422         cppit(cleanedImakefile, Template, ImakefileC, tmpfd, tmpMakefile);
423
424         if (show) {
425                 if (Makefile == NULL)
426                         showit(tmpfd);
427         } else
428                 makeit();
429         wrapup();
430         return 0;
431 }
432
433 static void
434 showit(FILE *fd)
435 {
436         char    buf[ BUFSIZ ];
437         int     red;
438
439         fseek(fd, 0, 0);
440         while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
441                 writetmpfile(stdout, buf, red, "stdout");
442         if (red < 0)
443             LogFatal("Cannot read %s.", tmpMakefile);
444 }
445
446 static void
447 wrapup(void)
448 {
449         if (tmpMakefile != Makefile)
450                 unlink(tmpMakefile);
451         if (cleanedImakefile && cleanedImakefile != Imakefile)
452                 unlink(cleanedImakefile);
453         if (haveImakefileC)
454                 unlink(ImakefileC);
455 }
456
457 static
458 #ifdef SIGNALRETURNSINT
459 int
460 #else
461 void
462 #endif
463 catch(int sig)
464 {
465         errno = 0;
466         LogFatal("Signal %d.", sig);
467 }
468
469 /*
470  * Initialize some variables.
471  */
472 static void
473 init(void)
474 {
475         char    *p;
476
477         make_argindex=0;
478         while (make_argv[ make_argindex ] != NULL)
479                 make_argindex++;
480         cpp_argindex = 0;
481         while (cpp_argv[ cpp_argindex ] != NULL)
482                 cpp_argindex++;
483
484         /*
485          * See if the standard include directory is different than
486          * the default.  Or if cpp is not the default.  Or if the make
487          * found by the PATH variable is not the default.
488          */
489         if ((p = getenv("IMAKEINCLUDE"))) {
490                 if (*p != '-' || *(p+1) != 'I')
491                         LogFatal("Environment var IMAKEINCLUDE %s must begin with -I");
492                 AddCppArg(p);
493                 for (; *p; p++)
494                         if (*p == ' ') {
495                                 *p++ = '\0';
496                                 AddCppArg(p);
497                         }
498         }
499         if ((p = getenv("IMAKECPP")))
500                 cpp = p;
501         if ((p = getenv("IMAKEMAKE")))
502                 make_argv[0] = p;
503
504         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
505                 signal(SIGINT, catch);
506 }
507
508 static void
509 AddMakeArg(char *arg)
510 {
511         errno = 0;
512         if (make_argindex >= ARGUMENTS-1)
513                 LogFatal("Out of internal storage.");
514         make_argv[ make_argindex++ ] = arg;
515         make_argv[ make_argindex ] = NULL;
516 }
517
518 static void
519 AddCppArg(char *arg)
520 {
521         errno = 0;
522         if (cpp_argindex >= ARGUMENTS-1)
523                 LogFatal("Out of internal storage.");
524         cpp_argv[ cpp_argindex++ ] = arg;
525         cpp_argv[ cpp_argindex ] = NULL;
526 }
527
528 static void
529 SetOpts(int argc, char **argv)
530 {
531         errno = 0;
532         /*
533          * Now gather the arguments for make
534          */
535         for(argc--, argv++; argc; argc--, argv++) {
536             /*
537              * We intercept these flags.
538              */
539             if (argv[0][0] == '-') {
540                 if (argv[0][1] == 'D') {
541                     AddCppArg(argv[0]);
542                 } else if (argv[0][1] == 'I') {
543                     AddCppArg(argv[0]);
544                 } else if (argv[0][1] == 'f') {
545                     if (argv[0][2])
546                         Imakefile = argv[0]+2;
547                     else {
548                         argc--, argv++;
549                         if (! argc)
550                             LogFatal("No description arg after -f flag");
551                         Imakefile = argv[0];
552                     }
553                 } else if (argv[0][1] == 's') {
554                     if (argv[0][2])
555                         Makefile = ((argv[0][2] == '-') && !argv[0][3]) ?
556                             NULL : argv[0]+2;
557                     else {
558                         argc--, argv++;
559                         if (!argc)
560                             LogFatal("No description arg after -s flag");
561                         Makefile = ((argv[0][0] == '-') && !argv[0][1]) ?
562                             NULL : argv[0];
563                     }
564                     show = TRUE;
565                 } else if (argv[0][1] == 'e') {
566                    Makefile = (argv[0][2] ? argv[0]+2 : NULL);
567                    show = FALSE;
568                 } else if (argv[0][1] == 'T') {
569                     if (argv[0][2])
570                         Template = argv[0]+2;
571                     else {
572                         argc--, argv++;
573                         if (! argc)
574                             LogFatal("No description arg after -T flag");
575                         Template = argv[0];
576                     }
577                 } else if (argv[0][1] == 'C') {
578                     if (argv[0][2])
579                         ImakefileC = argv[0]+2;
580                     else {
581                         argc--, argv++;
582                         if (! argc)
583                             LogFatal("No imakeCfile arg after -C flag");
584                         ImakefileC = argv[0];
585                     }
586                 } else if (argv[0][1] == 'v') {
587                     verbose = TRUE;
588                 } else
589                     AddMakeArg(argv[0]);
590             } else
591                 AddMakeArg(argv[0]);
592         }
593 #ifdef USE_CC_E
594         if (!cpp)
595         {
596                 AddCppArg("-E");
597                 cpp = DEFAULT_CC;
598         }
599 #else
600         if (!cpp)
601                 cpp = DEFAULT_CPP;
602 #endif
603         cpp_argv[0] = cpp;
604         AddCppArg(ImakefileC);
605 }
606
607 static char *
608 FindImakefile(char *Imakefile)
609 {
610         if (Imakefile) {
611                 if (access(Imakefile, R_OK) < 0)
612                         LogFatal("Cannot find %s.", Imakefile);
613         } else {
614                 if (access("Imakefile", R_OK) < 0)
615                         if (access("imakefile", R_OK) < 0)
616                                 LogFatal("No description file.");
617                         else
618                                 Imakefile = "imakefile";
619                 else
620                         Imakefile = "Imakefile";
621         }
622         return(Imakefile);
623 }
624
625 static void
626 LogFatal(const char *s, ...)
627 {
628     static boolean entered = FALSE;
629     va_list args;
630
631     if (entered)
632         return;
633
634     entered = TRUE;
635
636     va_start(args, s);
637     Log(s, args);
638     va_end(args);
639
640     fprintf(stderr, "Stop.\n");
641
642     wrapup();
643
644     exit(1);
645 }
646
647 static void
648 LogMsg(const char *s, ...)
649 {
650     va_list args;
651
652     va_start(args, s);
653     Log(s, args);
654     va_end(args);
655 }
656
657 static void
658 Log(const char *s, va_list args)
659 {
660     int error_number = errno;
661
662     if (error_number) {
663         fprintf(stderr, "%s: ", program);
664         fprintf(stderr, "%s\n", strerror(error_number));
665     }
666     fprintf(stderr, "%s: ", program);
667     vfprintf(stderr, s, args);
668     fprintf(stderr, "\n");
669 }
670
671 static void
672 showargs(char **argv)
673 {
674         for (; *argv; argv++)
675                 fprintf(stderr, "%s ", *argv);
676         fprintf(stderr, "\n");
677 }
678
679 #define ImakefileCHeader "/* imake - temporary file */"
680
681 static void
682 CheckImakefileC(const char *masterc)
683 {
684         char mkcbuf[1024];
685         FILE *inFile;
686
687         if (access(masterc, F_OK) == 0) {
688                 inFile = fopen(masterc, "r");
689                 if (inFile == NULL)
690                         LogFatal("Refuse to overwrite: %s", masterc);
691                 if ((fgets(mkcbuf, sizeof(mkcbuf), inFile) &&
692                      strncmp(mkcbuf, ImakefileCHeader, 
693                              sizeof(ImakefileCHeader)-1)))
694                 {
695                         fclose(inFile);
696                         LogFatal("Refuse to overwrite: %s", masterc);
697                 }
698                 fclose(inFile);
699         }
700 }
701
702 #define LocalDefineFmt  "#define %s \"%s\"\n"
703 #define IncludeFmt      "#include %s\n"
704 #define ImakeDefSym     "INCLUDE_IMAKEFILE"
705 #define ImakeTmplSym    "IMAKE_TEMPLATE"
706 #define OverrideWarning "Warning: local file \"%s\" overrides global macros."
707
708 static boolean
709 optional_include(FILE *inFile, const char *defsym, const char *fname)
710 {
711         errno = 0;
712         if (access(fname, R_OK) == 0) {
713                 if(errno)
714                         LogMsg(OverrideWarning, fname);
715                 return (fprintf(inFile, LocalDefineFmt, defsym, fname) < 0 ||
716                         fprintf(inFile, IncludeFmt, defsym) < 0);
717         }
718         return FALSE;
719 }
720
721 static void
722 doit(FILE *outfd, const char *cmd, char **argv)
723 {
724         int     pid;
725         waitType        status;
726
727         /*
728          * Fork and exec the command.
729          */
730 #ifdef WIN32
731         if (outfd)
732                 dup2(fileno(outfd), 1);
733         status = _spawnvp(_P_WAIT, cmd, argv);
734         if (status < 0)
735                 LogFatal("Cannot spawn %s.", cmd);
736         if (status > 0)
737                 LogFatal("Exit code %d.", status);
738 #else
739         pid = fork();
740         if (pid < 0)
741                 LogFatal("Cannot fork.");
742         if (pid) {      /* parent... simply wait */
743                 while (wait(&status) > 0) {
744                         errno = 0;
745                         if (WIFSIGNALED(status))
746                                 LogFatal("Signal %d.", waitSig(status));
747                         if (WIFEXITED(status) && waitCode(status))
748                                 LogFatal("Exit code %d.", waitCode(status));
749                 }
750         }
751         else {  /* child... dup and exec cmd */
752                 if (verbose)
753                         showargs(argv);
754                 if (outfd)
755                         dup2(fileno(outfd), 1);
756                 execvp(cmd, argv);
757                 LogFatal("Cannot exec %s.", cmd);
758         }
759 #endif
760 }
761
762 #ifndef WIN32
763
764 #if (defined(DEFAULT_OS_NAME) || defined(DEFAULT_OS_MAJOR_REV) || \
765      defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
766 static void
767 parse_utsname(struct utsname *name, const char *fmt, char *result, const char *msg)
768 {
769   char buf[SYS_NMLN * 5 + 1];
770   char *ptr = buf;
771   int arg;
772
773   /* Assemble all the pieces into a buffer. */
774   for (arg = 0; fmt[arg] != ' '; arg++)
775     {
776       /* Our buffer is only guaranteed to hold 5 arguments. */
777       if (arg >= 5)
778         LogFatal(msg, fmt);
779
780       switch (fmt[arg])
781         {
782         case 's':
783           if (arg > 0)
784             *ptr++ = ' ';
785           strncpy(ptr, name->sysname, SYS_NMLN);
786           ptr += strlen(ptr);
787           break;
788
789         case 'n':
790           if (arg > 0)
791             *ptr++ = ' ';
792           strncpy(ptr, name->nodename, SYS_NMLN);
793           ptr += strlen(ptr);
794           break;
795
796         case 'r':
797           if (arg > 0)
798             *ptr++ = ' ';
799           strncpy(ptr, name->release, SYS_NMLN);
800           ptr += strlen(ptr);
801           break;
802
803         case 'v':
804           if (arg > 0)
805             *ptr++ = ' ';
806           strncpy(ptr, name->version, SYS_NMLN);
807           ptr += strlen(ptr);
808           break;
809
810         case 'm':
811           if (arg > 0)
812             *ptr++ = ' ';
813           strncpy(ptr, name->machine, SYS_NMLN);
814           ptr += strlen(ptr);
815           break;
816
817         default:
818           LogFatal(msg, fmt);
819         }
820     }
821
822   /* Just in case... */
823   if (strlen(buf) >= sizeof(buf))
824     LogFatal("Buffer overflow parsing uname.");
825
826   /* Parse the buffer.  The sscanf() return value is rarely correct. */
827   *result = '\0';
828   int ret = sscanf(buf, fmt + arg + 1, result);
829   (void) ret;
830 }
831 #endif
832
833 /* Trim leading 0's and periods from version names.  The 0's cause
834    the number to be interpreted as octal numbers.  Some version strings
835    have the potential for different numbers of .'s in them.
836  */
837         
838 #if (defined(DEFAULT_OS_MAJOR_REV) || defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
839 static const char *
840 trim_version(const char *p)
841 {
842
843         if (p != 0 && *p != '\0')
844         {
845                 while ((*p == '0' || *p == '.') && *(p + 1) != '\0')
846                         ++p;
847         }
848         return (p);
849 }
850 #endif
851
852 #endif
853
854 #ifdef linux
855 static void
856 get_distrib(FILE *inFile)
857 {
858   struct stat sb;
859
860   static char* yast = "/sbin/YaST";
861   static char* redhat = "/etc/redhat-release";
862
863   fprintf (inFile, "%s\n", "#define LinuxUnknown    0");
864   fprintf (inFile, "%s\n", "#define LinuxSuSE       1");
865   fprintf (inFile, "%s\n", "#define LinuxCaldera    2");
866   fprintf (inFile, "%s\n", "#define LinuxCraftworks 3");
867   fprintf (inFile, "%s\n", "#define LinuxDebian     4");
868   fprintf (inFile, "%s\n", "#define LinuxInfoMagic  5");
869   fprintf (inFile, "%s\n", "#define LinuxKheops     6");
870   fprintf (inFile, "%s\n", "#define LinuxPro        7");
871   fprintf (inFile, "%s\n", "#define LinuxRedHat     8");
872   fprintf (inFile, "%s\n", "#define LinuxSlackware  9");
873   fprintf (inFile, "%s\n", "#define LinuxTurbo      10");
874   fprintf (inFile, "%s\n", "#define LinuxWare       11");
875   fprintf (inFile, "%s\n", "#define LinuxYggdrasil  12");
876
877   if (lstat (yast, &sb) == 0) {
878     fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxSuSE");
879     return;
880   }
881   if (lstat (redhat, &sb) == 0) {
882     fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxRedHat");
883     return;
884   }
885   /* what's the definitive way to tell what any particular distribution is? */
886
887   fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxUnknown");
888   /* would like to know what version of the distribution it is */
889 }
890
891 static const char libc_c[]=
892 "#include <stdio.h>\n"
893 "#include <ctype.h>\n"
894 "\n"
895 "#if 0\n"
896 "#pragma weak gnu_get_libc_version\n"
897 "#pragma weak __libc_version\n"
898 "#pragma weak __linux_C_lib_version\n"
899 "#else\n"
900 "asm (\".weak gnu_get_libc_version\");\n"
901 "asm (\".weak __libc_version\");\n"
902 "asm (\".weak __linux_C_lib_version\");\n"
903 "#endif\n"
904 "\n"
905 "extern const char * gnu_get_libc_version (void);\n"
906 "extern const char * __linux_C_lib_version;\n"
907 "extern const char __libc_version [];\n"
908 "\n"
909 "int\n"
910 "main ()\n"
911 "{\n"
912 "  int libcmajor = 0, libcminor = 0, libcteeny = 0;\n"
913 "\n"
914 "  if (((&__linux_C_lib_version != 0)\n"
915 "       && ((&__libc_version != 0) || (gnu_get_libc_version != 0)))\n"
916 "      || (!(&__linux_C_lib_version != 0) && !(&__libc_version != 0)\n"
917 "         && !(gnu_get_libc_version != 0)))\n"
918 "  {\n"
919 "    libcmajor = 0;\n"
920 "    libcminor = 0;\n"
921 "    libcteeny = 0;\n"
922 "  }\n"
923 "  else\n"
924 "  {\n"
925 "    const char * ptr;\n"
926 "    int glibcmajor = 0;\n"
927 "\n"
928 "    if (gnu_get_libc_version != 0)\n"
929 "    {\n"
930 "      ptr = gnu_get_libc_version ();\n"
931 "      glibcmajor = 4;\n"
932 "    }\n"
933 "    else if (&__libc_version != 0)\n"
934 "    {\n"
935 "      ptr = __libc_version;\n"
936 "      glibcmajor = 4;\n"
937 "    }\n"
938 "    else\n"
939 "      ptr = __linux_C_lib_version;\n"
940 "\n"
941 "    while (!isdigit (*ptr))\n"
942 "      ptr++;\n"
943 "\n"
944 "    sscanf (ptr, \"%d.%d.%d\", &libcmajor, &libcminor, &libcteeny);\n"
945 "    libcmajor += glibcmajor;\n"
946 "  }\n"
947 "\n"
948 "  printf(\"#define DefaultLinuxCLibMajorVersion %d\\n\", libcmajor);\n"
949 "  printf(\"#define DefaultLinuxCLibMinorVersion %d\\n\", libcminor);\n"
950 "  printf(\"#define DefaultLinuxCLibTeenyVersion %d\\n\", libcteeny);\n"
951 "\n"
952 "  return 0;\n"
953 "}\n"
954 ;
955
956 #define libc32path "/usr/lib/libc.so"
957 #define libc64path "/usr/lib64/libc.so"
958
959 static void
960 get_libc_version(FILE *inFile)
961 {
962   char* libcso = NULL;
963   struct stat sb;
964   char buf[PATH_MAX];
965   char* ptr;
966   int libcmajor, libcminor, libcteeny;
967   struct utsname u;
968
969   /*
970    * If we are on a 64-bit Linux system and we see /usr/lib64/libc.so,
971    * we should use it.  Otherwise go with /usr/lib/libc.so.  It is entirely
972    * possible that someone will be running a 32-bit userland on a 64-bit
973    * system.
974    */
975   if (uname(&u) == -1) {
976     fprintf(stderr, "%s (%d): %s\n", __func__, __LINE__, strerror(errno));
977     abort();
978   }
979
980   if (!strcmp(u.sysname, "Linux") &&
981       (!strcmp(u.machine, "x86_64"))) {
982     if (!lstat (libc64path, &sb) && S_ISREG(sb.st_mode)) {
983       libcso = libc64path;
984     }
985   }
986
987   if (libcso == NULL) {
988     libcso = libc32path;
989   }
990
991   if (lstat (libcso, &sb) == 0) {
992     if (S_ISLNK (sb.st_mode)) {
993       /* 
994        * /usr/lib/libc.so is a symlink -- this is libc 5.x
995        * we can do this the quick way
996         */
997       if (readlink (libcso, buf, PATH_MAX) >= 0) {
998         for (ptr = buf; *ptr && !isdigit (*ptr); ptr++);
999         int ret = sscanf (ptr, "%d.%d.%d", &libcmajor, &libcminor, &libcteeny);
1000         (void) ret;
1001         fprintf(inFile, "#define DefaultLinuxCLibMajorVersion %d\n", libcmajor);
1002         fprintf(inFile, "#define DefaultLinuxCLibMinorVersion %d\n", libcminor);
1003         fprintf(inFile, "#define DefaultLinuxCLibTeenyVersion %d\n", libcteeny);
1004       }
1005     } else {
1006       /* 
1007        * /usr/lib/libc.so is NOT a symlink -- this is libc 6.x / glibc 2.x
1008        * now we have to figure this out the hard way.
1009        */
1010       char aout[PATH_MAX];
1011       int fd = -1;
1012       FILE *fp;
1013       const char *format = "%s -o %s -x c -";
1014       char *cc;
1015       int len;
1016       char *command;
1017
1018       memset(aout, '\0', PATH_MAX);
1019
1020       if (!lstat(getenv("TMPDIR"), &sb) && S_ISDIR(sb.st_mode))
1021         strncpy(aout, getenv("TMPDIR"), PATH_MAX - 1);
1022 #ifdef P_tmpdir /* defined by XPG and XOPEN, but don't assume we have it */
1023       else if (!lstat(P_tmpdir, &sb) && S_ISDIR(sb.st_mode))
1024         strncpy(aout, P_tmpdir, PATH_MAX - 1);
1025 #endif
1026       else if (!lstat("/tmp", &sb) && S_ISDIR(sb.st_mode))
1027         strncpy(aout, "/tmp", PATH_MAX - 1);
1028       else
1029         abort();
1030
1031       strncat(aout, "/imaketmp.XXXXXX", PATH_MAX - 1);
1032
1033       if ((fd = mkstemp(aout)) == -1)
1034         abort ();
1035
1036       if (close(fd) == -1)
1037         abort ();
1038
1039       cc = getenv ("CC");
1040       if (cc == NULL)
1041         cc = "gcc";
1042       len = strlen (aout) + strlen (format) + strlen (cc);
1043       if (len < 128) len = 128;
1044       command = alloca (len);
1045
1046       if (snprintf (command , len, format, cc, aout) == len)
1047         abort ();
1048
1049       fp = popen (command, "w");
1050       if (fp == NULL || fprintf (fp, "%s\n", libc_c) < 0 || pclose (fp) != 0)
1051         abort ();
1052
1053       fp = popen (aout, "r");
1054       if (fp == NULL)
1055         abort ();
1056
1057       while (fgets (command, len, fp))
1058         fprintf (inFile, "%s", command);
1059   
1060       len = pclose (fp);
1061       remove (aout);
1062       if (len)
1063         abort ();
1064     }
1065   }
1066 }
1067
1068 static void
1069 get_ld_version(FILE *inFile)
1070 {
1071   FILE* ldprog = popen ("ld -v", "r");
1072   char c;
1073   int ldmajor, ldminor;
1074
1075   if (ldprog) {
1076     do {
1077       c = fgetc (ldprog);
1078     } while (c != EOF && !isdigit (c));
1079     ungetc (c, ldprog);
1080     int ret = fscanf (ldprog, "%d.%d", &ldmajor, &ldminor);
1081     (void) ret;
1082     fprintf(inFile, "#define DefaultLinuxBinUtilsMajorVersion %d\n", 
1083             ldmajor * 10 + ldminor);    
1084     pclose (ldprog);
1085   }
1086 }
1087 #endif
1088
1089 #ifndef PATH_MAX
1090 #define PATH_MAX 1024
1091 #endif
1092
1093 #if defined(sun) && defined(__SVR4)
1094 static void
1095 get_sun_compiler_versions(FILE *inFile)
1096 {
1097   char buf[PATH_MAX];
1098   char cmd[PATH_MAX];
1099   static char* sunpro_cc = "/opt/solarisstudio/bin/cc";
1100   static char* sunpro_CC = "/opt/solarisstudio/bin/CC";
1101   static char* gnu_cc1 = "/usr/bin/gcc";
1102   static char* gnu_cc2 = "/usr/gnu/bin/gcc";
1103   static char* gnu_CC1 = "/usr/bin/g++";
1104   static char* gnu_CC2 = "/usr/gnu/bin/g++";
1105   int cmajor, cminor;
1106   char* vptr;
1107   struct stat sb;
1108   FILE* ccproc;
1109   int ret;
1110
1111   if (lstat (sunpro_cc, &sb) == 0) {
1112     strncpy (cmd, sunpro_cc, PATH_MAX);
1113     strncat (cmd, " -V 2>&1", 8);
1114     if ((ccproc = popen (cmd, "r")) != NULL) {
1115       if (fgets (buf, PATH_MAX, ccproc) != NULL) {
1116         vptr = strrchr (buf, 'C');
1117         for (; !isdigit(*vptr); vptr++);
1118         ret = sscanf (vptr, "%d.%d", &cmajor, &cminor);
1119         fprintf (inFile, 
1120                  "#define DefaultSunProCCompilerMajorVersion %d\n",
1121                  cmajor);
1122         fprintf (inFile, 
1123                  "#define DefaultSunProCCompilerMinorVersion %d\n",
1124                  cminor);
1125       }
1126       while (fgets (buf, PATH_MAX, ccproc) != NULL) {};
1127       pclose (ccproc);
1128     }
1129   }
1130   if (lstat (sunpro_CC, &sb) == 0) {
1131     strncpy (cmd, sunpro_CC, PATH_MAX);
1132     strncat (cmd, " -V 2>&1", 8);
1133     if ((ccproc = popen (cmd, "r")) != NULL) {
1134       if (fgets (buf, PATH_MAX, ccproc) != NULL) {
1135         vptr = strrchr (buf, 'C');
1136         for (; !isdigit(*vptr); vptr++);
1137         ret = sscanf (vptr, "%d.%d", &cmajor, &cminor);
1138         fprintf (inFile, 
1139                  "#define DefaultSunProCplusplusCompilerMajorVersion %d\n",
1140                  cmajor);
1141         fprintf (inFile, 
1142                  "#define DefaultSunProCplusplusCompilerMinorVersion %d\n",
1143                  cminor);
1144       }
1145       while (fgets (buf, PATH_MAX, ccproc) != NULL) {};
1146       pclose (ccproc);
1147     }
1148   }
1149   cmd[0] = '\0';
1150   if (lstat (gnu_cc1, &sb) == 0) {
1151     strncpy (cmd, gnu_cc1, PATH_MAX);
1152   }
1153   else if (lstat (gnu_cc2, &sb) == 0) {
1154     strncpy (cmd, gnu_cc2, PATH_MAX);
1155   }
1156   if (cmd[0] != '\0') {
1157     strncat (cmd, " --version 2>&1", 15);
1158     if ((ccproc = popen (cmd, "r")) != NULL) {
1159       if (fgets (buf, PATH_MAX, ccproc) != NULL) {
1160         vptr = strrchr (buf, ')');
1161         for (; !isdigit(*vptr); vptr++);
1162         ret = sscanf (vptr, "%d.%d", &cmajor, &cminor);
1163         fprintf (inFile,
1164                  "#define DefaultGnuCCompilerMajorVersion %d\n",
1165                  cmajor);
1166         fprintf (inFile,
1167                  "#define DefaultGnuCCompilerMinorVersion %d\n",
1168                  cminor);
1169       }
1170       while (fgets (buf, PATH_MAX, ccproc) != NULL) {};
1171       pclose (ccproc);
1172     }
1173   }
1174   cmd[0] = '\0';
1175   if (lstat (gnu_CC1, &sb) == 0) {
1176     strncpy (cmd, gnu_CC1, PATH_MAX);
1177   }
1178   else if (lstat (gnu_CC2, &sb) == 0) {
1179     strncpy (cmd, gnu_CC2, PATH_MAX);
1180   }
1181   if (cmd[0] != '\0') {
1182     strncat (cmd, " --version 2>&1", 15);
1183     if ((ccproc = popen (cmd, "r")) != NULL) {
1184       if (fgets (buf, PATH_MAX, ccproc) != NULL) {
1185         vptr = strrchr (buf, ')');
1186         for (; !isdigit(*vptr); vptr++);
1187         ret = sscanf (vptr, "%d.%d", &cmajor, &cminor);
1188         fprintf (inFile,
1189                  "#define DefaultGnuCplusplusCompilerMajorVersion %d\n",
1190                  cmajor);
1191         fprintf (inFile,
1192                  "#define DefaultGnuCplusplusCompilerMinorVersion %d\n",
1193                  cminor);
1194       }
1195       while (fgets (buf, PATH_MAX, ccproc) != NULL) {};
1196       pclose (ccproc);
1197     }
1198   }
1199   (void) ret;
1200 }
1201 #endif
1202
1203 static void
1204 get_gcc_incdir(FILE *inFile)
1205 {
1206   static char* gcc_path[] = {
1207 #ifdef linux
1208     "/usr/bin/cc",      /* for Linux PostIncDir */
1209 #endif
1210 #ifdef sun
1211     "/usr/bin/gcc",
1212     "/usr/gnu/bin/gcc",
1213 #endif
1214     "/usr/local/bin/gcc",
1215     "/opt/gnu/bin/gcc"
1216   };
1217   struct stat sb;
1218   int i;
1219   FILE* gccproc;
1220   char buf[PATH_MAX];
1221   char cmd[PATH_MAX];
1222   char* ptr;
1223
1224   memset(buf, 0, PATH_MAX);
1225   for (i = 0; i < sizeof gcc_path / sizeof gcc_path[0]; i++) {
1226     if (lstat (gcc_path[i], &sb) == 0) {
1227       strncpy (cmd, gcc_path[i], PATH_MAX - 1 );
1228       strncat (cmd, " --print-libgcc-file-name", PATH_MAX - 1);
1229       if ((gccproc = popen (cmd, "r")) != NULL) {
1230         if (fgets (buf, PATH_MAX - 1, gccproc) != NULL) {
1231           ptr = strstr (buf, "libgcc.a");
1232           if (ptr) strncpy (ptr, "include", 8);
1233         }
1234         (void) pclose (gccproc);
1235         break;
1236       }
1237     }
1238   }
1239   if (buf[0])
1240     fprintf (inFile, "#define DefaultGccIncludeDir %s\n", buf);
1241 }
1242
1243 static boolean
1244 define_os_defaults(FILE *inFile)
1245 {
1246 #ifndef WIN32
1247 #if (defined(DEFAULT_OS_NAME) || defined(DEFAULT_OS_MAJOR_REV) || \
1248      defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
1249         struct utsname name;
1250         char buf[SYS_NMLN * 5 + 1];
1251
1252         /* Obtain the system information. */
1253         if (uname(&name) < 0)
1254                 LogFatal("Cannot invoke uname");
1255
1256 # ifdef DEFAULT_OS_NAME
1257         parse_utsname(&name, DEFAULT_OS_NAME, buf, 
1258                       "Bad DEFAULT_OS_NAME syntax %s");
1259         if (buf[0] != '\0')
1260                 fprintf(inFile, "#define DefaultOSName %s\n", buf);
1261 # endif
1262
1263 # ifdef DEFAULT_OS_MAJOR_REV
1264         parse_utsname(&name, DEFAULT_OS_MAJOR_REV, buf,
1265                       "Bad DEFAULT_OS_MAJOR_REV syntax %s");
1266         fprintf(inFile, "#define DefaultOSMajorVersion %s\n", 
1267                 *buf ? trim_version(buf) : "0");
1268 # endif
1269
1270 # ifdef DEFAULT_OS_MINOR_REV
1271         parse_utsname(&name, DEFAULT_OS_MINOR_REV, buf,
1272                       "Bad DEFAULT_OS_MINOR_REV syntax %s");
1273         fprintf(inFile, "#define DefaultOSMinorVersion %s\n", 
1274                 *buf ? trim_version(buf) : "0");
1275 # endif
1276
1277 # ifdef DEFAULT_OS_TEENY_REV
1278         parse_utsname(&name, DEFAULT_OS_TEENY_REV, buf,
1279                       "Bad DEFAULT_OS_TEENY_REV syntax %s");
1280         fprintf(inFile, "#define DefaultOSTeenyVersion %s\n", 
1281                 *buf ? trim_version(buf) : "0");
1282 # endif
1283 #endif
1284 #ifdef linux
1285     get_distrib (inFile);
1286     get_libc_version (inFile);
1287     get_ld_version(inFile);
1288 #endif
1289     get_gcc_incdir(inFile);
1290 #if defined (sun) && defined(SVR4)
1291     get_sun_compiler_versions (inFile);
1292 #endif
1293 #else /* WIN32 */
1294    OSVERSIONINFO osvi;
1295    static char* os_names[] = { "Win32s", "Windows 95", "Windows NT" };
1296
1297    memset(&osvi, 0, sizeof(OSVERSIONINFO));
1298    osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
1299    GetVersionEx (&osvi);
1300
1301    fprintf (inFile, "#define DefaultOSName Microsoft %s\n", 
1302             os_names[osvi.dwPlatformId]);
1303
1304    fprintf(inFile, "#define DefaultOSMajorVersion %d\n", osvi.dwMajorVersion);
1305    fprintf(inFile, "#define DefaultOSMinorVersion %d\n", osvi.dwMinorVersion);
1306    fprintf(inFile, "#define DefaultOSTeenyVersion %d\n", 
1307            osvi.dwBuildNumber & 0xFFFF);
1308 #endif /* WIN32 */
1309    return FALSE;
1310 }
1311
1312 static void
1313 cppit(const char *imakefile, const char *template, const char *masterc, FILE *outfd, const char *outfname)
1314 {
1315         FILE    *inFile;
1316
1317         haveImakefileC = TRUE;
1318         inFile = fopen(masterc, "w");
1319         if (inFile == NULL)
1320                 LogFatal("Cannot open %s for output.", masterc);
1321         if (fprintf(inFile, "%s\n", ImakefileCHeader) < 0 ||
1322             define_os_defaults(inFile) ||
1323             optional_include(inFile, "IMAKE_LOCAL_DEFINES", "localdefines") ||
1324             optional_include(inFile, "IMAKE_ADMIN_DEFINES", "admindefines") ||
1325             fprintf(inFile, "#define %s <%s>\n", ImakeDefSym, imakefile) < 0 ||
1326             fprintf(inFile, LocalDefineFmt, ImakeTmplSym, template) < 0 ||
1327             fprintf(inFile, IncludeFmt, ImakeTmplSym) < 0 ||
1328             optional_include(inFile, "IMAKE_ADMIN_MACROS", "adminmacros") ||
1329             optional_include(inFile, "IMAKE_LOCAL_MACROS", "localmacros") ||
1330             fflush(inFile) ||
1331             fclose(inFile))
1332                 LogFatal("Cannot write to %s.", masterc);
1333         /*
1334          * Fork and exec cpp
1335          */
1336         doit(outfd, cpp, cpp_argv);
1337         CleanCppOutput(outfd, outfname);
1338 }
1339
1340 static void
1341 makeit(void)
1342 {
1343         doit(NULL, make_argv[0], make_argv);
1344 }
1345
1346 static char *
1347 CleanCppInput(char *imakefile)
1348 {
1349         FILE    *outFile = NULL;
1350         FILE    *inFile;
1351         char    *buf,           /* buffer for file content */
1352                 *pbuf,          /* walking pointer to buf */
1353                 *punwritten,    /* pointer to unwritten portion of buf */
1354                 *ptoken,        /* pointer to # token */
1355                 *pend,          /* pointer to end of # token */
1356                 savec;          /* temporary character holder */
1357         int     count;
1358         struct stat     st;
1359
1360         /*
1361          * grab the entire file.
1362          */
1363         if (!(inFile = fopen(imakefile, "r")))
1364                 LogFatal("Cannot open %s for input.", imakefile);
1365         if (fstat(fileno(inFile), &st) < 0)
1366                 LogFatal("Cannot stat %s for size.", imakefile);
1367         buf = Emalloc((int)st.st_size+3);
1368         count = fread(buf + 2, 1, st.st_size, inFile);
1369         if (count == 0  &&  st.st_size != 0)
1370                 LogFatal("Cannot read %s:", imakefile);
1371         fclose(inFile);
1372         buf[0] = '\n';
1373         buf[1] = '\n';
1374         buf[count + 2] = '\0';
1375
1376         punwritten = pbuf = buf + 2;
1377         while (*pbuf) {
1378             /* for compatibility, replace make comments for cpp */
1379             if (*pbuf == '#' && pbuf[-1] == '\n' && pbuf[-2] != '\\') {
1380                 ptoken = pbuf+1;
1381                 while (*ptoken == ' ' || *ptoken == '\t')
1382                         ptoken++;
1383                 pend = ptoken;
1384                 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
1385                         pend++;
1386                 savec = *pend;
1387                 *pend = '\0';
1388                 if (strcmp(ptoken, "define") &&
1389                     strcmp(ptoken, "if") &&
1390                     strcmp(ptoken, "ifdef") &&
1391                     strcmp(ptoken, "ifndef") &&
1392                     strcmp(ptoken, "include") &&
1393                     strcmp(ptoken, "line") &&
1394                     strcmp(ptoken, "else") &&
1395                     strcmp(ptoken, "elif") &&
1396                     strcmp(ptoken, "endif") &&
1397                     strcmp(ptoken, "error") &&
1398                     strcmp(ptoken, "pragma") &&
1399                     strcmp(ptoken, "undef")) {
1400                     if (outFile == NULL) {
1401                         tmpImakefile = Strdup(tmpImakefile);
1402                         int ret = mkstemp(tmpImakefile);
1403                         (void) ret;
1404                         outFile = fopen(tmpImakefile, "w");
1405                         if (outFile == NULL)
1406                             LogFatal("Cannot open %s for write.",
1407                                 tmpImakefile);
1408                     }
1409                     writetmpfile(outFile, punwritten, pbuf-punwritten,
1410                                  tmpImakefile);
1411                     if (ptoken > pbuf + 1)
1412                         writetmpfile(outFile, "XCOMM", 5, tmpImakefile);
1413                     else
1414                         writetmpfile(outFile, "XCOMM ", 6, tmpImakefile);
1415                     punwritten = pbuf + 1;
1416                 }
1417                 *pend = savec;
1418             }
1419             pbuf++;
1420         }
1421         if (outFile) {
1422             writetmpfile(outFile, punwritten, pbuf-punwritten, tmpImakefile);
1423             fclose(outFile);
1424             return tmpImakefile;
1425         }
1426
1427         return(imakefile);
1428 }
1429
1430 static void
1431 CleanCppOutput(FILE *tmpfd, const char *tmpfname)
1432 {
1433         char    *input;
1434         int     blankline = 0;
1435
1436         while ((input = ReadLine(tmpfd, tmpfname))) {
1437                 if (isempty(input)) {
1438                         if (blankline++)
1439                                 continue;
1440                         KludgeResetRule();
1441                 } else {
1442                         blankline = 0;
1443                         KludgeOutputLine(&input);
1444                         writetmpfile(tmpfd, input, strlen(input), tmpfname);
1445                 }
1446                 writetmpfile(tmpfd, "\n", 1, tmpfname);
1447         }
1448         fflush(tmpfd);
1449 #ifdef NFS_STDOUT_BUG
1450         /*
1451          * On some systems, NFS seems to leave a large number of nulls at
1452          * the end of the file.  Ralph Swick says that this kludge makes the
1453          * problem go away.
1454          */
1455         ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
1456 #endif
1457 }
1458
1459 /*
1460  * Determine if a line has nothing in it.  As a side effect, we trim white
1461  * space from the end of the line.  Cpp magic cookies are also thrown away.
1462  * "XCOMM" token is transformed to "#".
1463  */
1464 static boolean
1465 isempty(char *line)
1466 {
1467         char    *pend;
1468
1469         /*
1470          * Check for lines of the form
1471          *      # n "...
1472          * or
1473          *      # line n "...
1474          */
1475         if (*line == '#') {
1476                 pend = line+1;
1477                 if (*pend == ' ')
1478                         pend++;
1479                 if (*pend == 'l' && pend[1] == 'i' && pend[2] == 'n' &&
1480                     pend[3] == 'e' && pend[4] == ' ')
1481                         pend += 5;
1482                 if (isdigit((int)*pend)) {
1483                         do {
1484                             pend++;
1485                         } while (isdigit((int)*pend));
1486                         if (*pend == '\n' || *pend == '\0')
1487                                 return(TRUE);
1488                         if (*pend++ == ' ' && *pend == '"')
1489                                 return(TRUE);
1490                 }
1491                 while (*pend)
1492                     pend++;
1493         } else {
1494             for (pend = line; *pend; pend++) {
1495                 if (*pend == 'X' && pend[1] == 'C' && pend[2] == 'O' &&
1496                     pend[3] == 'M' && pend[4] == 'M' &&
1497                     (pend == line || pend[-1] == ' ' || pend[-1] == '\t') &&
1498                     (pend[5] == ' ' || pend[5] == '\t' || pend[5] == '\0'))
1499                 {
1500                     *pend = '#';
1501                     strncpy(pend+1, pend+5, 1);
1502                 }
1503 #ifdef MAGIC_MAKE_VARS
1504                 if (*pend == 'X' && pend[1] == 'V' && pend[2] == 'A' &&
1505                     pend[3] == 'R')
1506                 {
1507                     char varbuf[5];
1508                     int i;
1509
1510                     if (pend[4] == 'd' && pend[5] == 'e' && pend[6] == 'f' &&
1511                         pend[7] >= '0' && pend[7] <= '9')
1512                     {
1513                         i = pend[7] - '0';
1514                         snprintf(varbuf, 5, "%0.4d", xvariable);
1515                         strncpy(pend+4, varbuf, 4);
1516                         xvariables[i] = xvariable;
1517                         xvariable = (xvariable + 1) % 10000;
1518                     }
1519                     else if (pend[4] == 'u' && pend[5] == 's' &&
1520                              pend[6] == 'e' && pend[7] >= '0' &&
1521                              pend[7] <= '9')
1522                     {
1523                         i = pend[7] - '0';
1524                         snprintf(varbuf, 5, "%0.4d", xvariables[i]);
1525                         strncpy(pend+4, varbuf, 4);
1526                     }
1527                 }
1528 #endif
1529             }
1530         }
1531         while (--pend >= line && (*pend == ' ' || *pend == '\t')) ;
1532         pend[1] = '\0';
1533         return (*line == '\0');
1534 }
1535
1536 /*ARGSUSED*/
1537 static char *
1538 ReadLine(FILE *tmpfd, const char *tmpfname)
1539 {
1540         static boolean  initialized = FALSE;
1541         static char     *buf, *pline, *end;
1542         char    *p1, *p2;
1543
1544         if (! initialized) {
1545 #ifdef WIN32
1546                 FILE *fp = tmpfd;
1547 #endif
1548                 int     total_red;
1549                 struct stat     st;
1550
1551                 /*
1552                  * Slurp it all up.
1553                  */
1554                 fseek(tmpfd, 0, 0);
1555                 if (fstat(fileno(tmpfd), &st) < 0)
1556                         LogFatal("cannot stat %s for size", tmpMakefile);
1557                 pline = buf = Emalloc((int)st.st_size+1);
1558                 total_red = fread(buf, 1, st.st_size, tmpfd);
1559                 if (total_red == 0  &&  st.st_size != 0)
1560                         LogFatal("cannot read %s", tmpMakefile);
1561                 end = buf + total_red;
1562                 *end = '\0';
1563                 fseek(tmpfd, 0, 0);
1564 #if defined(SYSV) || defined(WIN32)
1565                 tmpfd = freopen(tmpfname, "w+", tmpfd);
1566 #ifdef WIN32
1567                 if (! tmpfd) /* if failed try again */
1568                         tmpfd = freopen(tmpfname, "w+", fp);
1569 #endif
1570                 if (! tmpfd)
1571                         LogFatal("cannot reopen %s.", tmpfname);
1572 #else   /* !SYSV */
1573                 int ret = ftruncate(fileno(tmpfd), (off_t) 0);
1574                 (void) ret;
1575 #endif  /* !SYSV */
1576                 initialized = TRUE;
1577             fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
1578             fprintf (tmpfd, "# %s\n",
1579                 "$TOG: imake.c /main/104 1998/03/24 12:45:15 kaleb $");
1580         }
1581
1582         for (p1 = pline; p1 < end; p1++) {
1583                 if (*p1 == '@' && *(p1+1) == '@'
1584                     /* ignore ClearCase version-extended pathnames */
1585                     && !(p1 != pline && !isspace((int)*(p1-1))
1586                     && *(p1+2) == '/'))
1587                 { /* soft EOL */
1588                         *p1++ = '\0';
1589                         p1++; /* skip over second @ */
1590                         break;
1591                 }
1592                 else if (*p1 == '\n') { /* real EOL */
1593 #ifdef WIN32
1594                         if (p1 > pline && p1[-1] == '\r')
1595                                 p1[-1] = '\0';
1596 #endif
1597                         *p1++ = '\0';
1598                         break;
1599                 }
1600         }
1601
1602         /*
1603          * return NULL at the end of the file.
1604          */
1605         p2 = (pline == p1 ? NULL : pline);
1606         pline = p1;
1607         return(p2);
1608 }
1609
1610 static void
1611 writetmpfile(FILE *fd, const char *buf, int cnt, const char *fname)
1612 {
1613         if (fwrite(buf, sizeof(char), cnt, fd) == -1)
1614                 LogFatal("Cannot write to %s.", fname);
1615 }
1616
1617 static char *
1618 Emalloc(int size)
1619 {
1620         char    *p;
1621
1622         if ((p = malloc(size)) == NULL)
1623                 LogFatal("Cannot allocate %d bytes.", size);
1624         return(p);
1625 }
1626
1627 #ifdef FIXUP_CPP_WHITESPACE
1628 static void
1629 KludgeOutputLine(char **pline)
1630 {
1631         char    *p = *pline;
1632         char    quotechar = '\0';
1633
1634         switch (*p) {
1635             case '#':   /*Comment - ignore*/
1636                 break;
1637             case '\t':  /*Already tabbed - ignore it*/
1638                 break;
1639             case ' ':   /*May need a tab*/
1640             default:
1641 # ifdef INLINE_SYNTAX
1642                 if (*p == '<' && p[1] == '<') { /* inline file close */
1643                     InInline--;
1644                     InRule = TRUE;
1645                     break;
1646                 }
1647 # endif
1648                 /*
1649                  * The following cases should not be treated as beginning of 
1650                  * rules:
1651                  * variable := name     (GNU make)
1652                  * variable = .*:.*     (':' should be allowed as value)
1653                  *      sed 's:/a:/b:'  (: used in quoted values)
1654                  */
1655                 for (; *p; p++) {
1656                     if (quotechar) {
1657                         if (quotechar == '\\' ||
1658                             (*p == quotechar &&
1659 # ifdef WIN32
1660                              quotechar != ')' &&
1661 # endif
1662                              p[-1] != '\\'))
1663                             quotechar = '\0';
1664                         continue;
1665                     }
1666                     switch (*p) {
1667                     case '\\':
1668                     case '"':
1669                     case '\'':
1670                         quotechar = *p;
1671                         break;
1672                     case '(':
1673                         quotechar = ')';
1674                         break;
1675                     case '{':
1676                         quotechar = '}';
1677                         break;
1678                     case '[':
1679                         quotechar = ']';
1680                         break;
1681                     case '=':
1682 # ifdef REMOVE_CPP_LEADSPACE
1683                         if (!InRule && **pline == ' ') {
1684                             while (**pline == ' ')
1685                                 (*pline)++;
1686                         }
1687 # endif
1688                         goto breakfor;
1689 # ifdef INLINE_SYNTAX
1690                     case '<':
1691                         if (p[1] == '<') /* inline file start */
1692                             InInline++;
1693                         break;
1694 # endif
1695                     case ':':
1696                         if (p[1] == '=')
1697                             goto breakfor;
1698                         while (**pline == ' ')
1699                             (*pline)++;
1700                         InRule = TRUE;
1701                         return;
1702                     }
1703                 }
1704 breakfor:
1705                 if (InRule && **pline == ' ')
1706                     **pline = '\t';
1707                 break;
1708         }
1709 }
1710
1711 static void
1712 KludgeResetRule(void)
1713 {
1714         InRule = FALSE;
1715 }
1716 #endif /* FIXUP_CPP_WHITESPACE */
1717
1718 static char *
1719 Strdup(const char *cp)
1720 {
1721         char *new = Emalloc(strlen(cp) + 1);
1722
1723         memcpy(new, cp, strlen(cp) + 1);
1724         return new;
1725 }