1bb833513a2cf7f656fde35eb5d62bf829232142
[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 librararies 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);
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);
1025 #endif
1026       else if (!lstat("/tmp", &sb) && S_ISDIR(sb.st_mode))
1027         strncpy(aout, "/tmp", PATH_MAX);
1028       else
1029         abort();
1030
1031       strncpy(aout+strlen(aout), "/imaketmp.XXXXXX", 16);
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/SUNWspro/bin/cc";
1100   static char* sunpro_CC = "/opt/SUNWspro/bin/CC";
1101   int cmajor, cminor;
1102   char* vptr;
1103   struct stat sb;
1104   FILE* ccproc;
1105   int ret;
1106
1107   if (lstat (sunpro_cc, &sb) == 0) {
1108     strncpy (cmd, sunpro_cc, PATH_MAX);
1109     strncat (cmd, " -V 2>&1", 8);
1110     if ((ccproc = popen (cmd, "r")) != NULL) {
1111       if (fgets (buf, PATH_MAX, ccproc) != NULL) {
1112         vptr = strrchr (buf, 'C');
1113         for (; !isdigit(*vptr); vptr++);
1114         ret = sscanf (vptr, "%d.%d", &cmajor, &cminor);
1115         fprintf (inFile, 
1116                  "#define DefaultSunProCCompilerMajorVersion %d\n",
1117                  cmajor);
1118         fprintf (inFile, 
1119                  "#define DefaultSunProCCompilerMinorVersion %d\n",
1120                  cminor);
1121       }
1122       while (fgets (buf, PATH_MAX, ccproc) != NULL) {};
1123       pclose (ccproc);
1124     }
1125   }
1126   if (lstat (sunpro_CC, &sb) == 0) {
1127     strncpy (cmd, sunpro_CC, PATH_MAX);
1128     strncat (cmd, " -V 2>&1", 8);
1129     if ((ccproc = popen (cmd, "r")) != NULL) {
1130       if (fgets (buf, PATH_MAX, ccproc) != NULL) {
1131         vptr = strrchr (buf, 'C');
1132         for (; !isdigit(*vptr); vptr++);
1133         ret = sscanf (vptr, "%d.%d", &cmajor, &cminor);
1134         fprintf (inFile, 
1135                  "#define DefaultSunProCplusplusCompilerMajorVersion %d\n",
1136                  cmajor);
1137         fprintf (inFile, 
1138                  "#define DefaultSunProCplusplusCompilerMinorVersion %d\n",
1139                  cminor);
1140       }
1141       while (fgets (buf, PATH_MAX, ccproc) != NULL) {};
1142       pclose (ccproc);
1143     }
1144   }
1145   (void) ret;
1146 }
1147 #endif
1148
1149 static void
1150 get_gcc_incdir(FILE *inFile)
1151 {
1152   static char* gcc_path[] = {
1153 #ifdef linux
1154     "/usr/bin/cc",      /* for Linux PostIncDir */
1155 #endif
1156     "/usr/local/bin/gcc",
1157     "/opt/gnu/bin/gcc"
1158   };
1159   struct stat sb;
1160   int i;
1161   FILE* gccproc;
1162   char buf[PATH_MAX];
1163   char cmd[PATH_MAX];
1164   char* ptr;
1165
1166   buf[0] = '\0';
1167   for (i = 0; i < sizeof gcc_path / sizeof gcc_path[0]; i++) {
1168     if (lstat (gcc_path[i], &sb) == 0) {
1169       strncpy (cmd, gcc_path[i], PATH_MAX - 25);
1170       strncpy (cmd + strlen(cmd), " --print-libgcc-file-name", 25);
1171       if ((gccproc = popen (cmd, "r")) != NULL) {
1172         if (fgets (buf, PATH_MAX, gccproc) != NULL) {
1173           ptr = strstr (buf, "libgcc.a");
1174           if (ptr) strncpy (ptr, "include", 7);
1175         }
1176         (void) pclose (gccproc);
1177         break;
1178       }
1179     }
1180   }
1181   if (buf[0])
1182     fprintf (inFile, "#define DefaultGccIncludeDir %s\n", buf);
1183 }
1184
1185 static boolean
1186 define_os_defaults(FILE *inFile)
1187 {
1188 #ifndef WIN32
1189 #if (defined(DEFAULT_OS_NAME) || defined(DEFAULT_OS_MAJOR_REV) || \
1190      defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
1191         struct utsname name;
1192         char buf[SYS_NMLN * 5 + 1];
1193
1194         /* Obtain the system information. */
1195         if (uname(&name) < 0)
1196                 LogFatal("Cannot invoke uname");
1197
1198 # ifdef DEFAULT_OS_NAME
1199         parse_utsname(&name, DEFAULT_OS_NAME, buf, 
1200                       "Bad DEFAULT_OS_NAME syntax %s");
1201         if (buf[0] != '\0')
1202                 fprintf(inFile, "#define DefaultOSName %s\n", buf);
1203 # endif
1204
1205 # ifdef DEFAULT_OS_MAJOR_REV
1206         parse_utsname(&name, DEFAULT_OS_MAJOR_REV, buf,
1207                       "Bad DEFAULT_OS_MAJOR_REV syntax %s");
1208         fprintf(inFile, "#define DefaultOSMajorVersion %s\n", 
1209                 *buf ? trim_version(buf) : "0");
1210 # endif
1211
1212 # ifdef DEFAULT_OS_MINOR_REV
1213         parse_utsname(&name, DEFAULT_OS_MINOR_REV, buf,
1214                       "Bad DEFAULT_OS_MINOR_REV syntax %s");
1215         fprintf(inFile, "#define DefaultOSMinorVersion %s\n", 
1216                 *buf ? trim_version(buf) : "0");
1217 # endif
1218
1219 # ifdef DEFAULT_OS_TEENY_REV
1220         parse_utsname(&name, DEFAULT_OS_TEENY_REV, buf,
1221                       "Bad DEFAULT_OS_TEENY_REV syntax %s");
1222         fprintf(inFile, "#define DefaultOSTeenyVersion %s\n", 
1223                 *buf ? trim_version(buf) : "0");
1224 # endif
1225 #endif
1226 #ifdef linux
1227     get_distrib (inFile);
1228     get_libc_version (inFile);
1229     get_ld_version(inFile);
1230 #endif
1231     get_gcc_incdir(inFile);
1232 #if defined (sun) && defined(SVR4)
1233     get_sun_compiler_versions (inFile);
1234 #endif
1235 #else /* WIN32 */
1236    OSVERSIONINFO osvi;
1237    static char* os_names[] = { "Win32s", "Windows 95", "Windows NT" };
1238
1239    memset(&osvi, 0, sizeof(OSVERSIONINFO));
1240    osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
1241    GetVersionEx (&osvi);
1242
1243    fprintf (inFile, "#define DefaultOSName Microsoft %s\n", 
1244             os_names[osvi.dwPlatformId]);
1245
1246    fprintf(inFile, "#define DefaultOSMajorVersion %d\n", osvi.dwMajorVersion);
1247    fprintf(inFile, "#define DefaultOSMinorVersion %d\n", osvi.dwMinorVersion);
1248    fprintf(inFile, "#define DefaultOSTeenyVersion %d\n", 
1249            osvi.dwBuildNumber & 0xFFFF);
1250 #endif /* WIN32 */
1251    return FALSE;
1252 }
1253
1254 static void
1255 cppit(const char *imakefile, const char *template, const char *masterc, FILE *outfd, const char *outfname)
1256 {
1257         FILE    *inFile;
1258
1259         haveImakefileC = TRUE;
1260         inFile = fopen(masterc, "w");
1261         if (inFile == NULL)
1262                 LogFatal("Cannot open %s for output.", masterc);
1263         if (fprintf(inFile, "%s\n", ImakefileCHeader) < 0 ||
1264             define_os_defaults(inFile) ||
1265             optional_include(inFile, "IMAKE_LOCAL_DEFINES", "localdefines") ||
1266             optional_include(inFile, "IMAKE_ADMIN_DEFINES", "admindefines") ||
1267             fprintf(inFile, "#define %s <%s>\n", ImakeDefSym, imakefile) < 0 ||
1268             fprintf(inFile, LocalDefineFmt, ImakeTmplSym, template) < 0 ||
1269             fprintf(inFile, IncludeFmt, ImakeTmplSym) < 0 ||
1270             optional_include(inFile, "IMAKE_ADMIN_MACROS", "adminmacros") ||
1271             optional_include(inFile, "IMAKE_LOCAL_MACROS", "localmacros") ||
1272             fflush(inFile) ||
1273             fclose(inFile))
1274                 LogFatal("Cannot write to %s.", masterc);
1275         /*
1276          * Fork and exec cpp
1277          */
1278         doit(outfd, cpp, cpp_argv);
1279         CleanCppOutput(outfd, outfname);
1280 }
1281
1282 static void
1283 makeit(void)
1284 {
1285         doit(NULL, make_argv[0], make_argv);
1286 }
1287
1288 static char *
1289 CleanCppInput(char *imakefile)
1290 {
1291         FILE    *outFile = NULL;
1292         FILE    *inFile;
1293         char    *buf,           /* buffer for file content */
1294                 *pbuf,          /* walking pointer to buf */
1295                 *punwritten,    /* pointer to unwritten portion of buf */
1296                 *ptoken,        /* pointer to # token */
1297                 *pend,          /* pointer to end of # token */
1298                 savec;          /* temporary character holder */
1299         int     count;
1300         struct stat     st;
1301
1302         /*
1303          * grab the entire file.
1304          */
1305         if (!(inFile = fopen(imakefile, "r")))
1306                 LogFatal("Cannot open %s for input.", imakefile);
1307         if (fstat(fileno(inFile), &st) < 0)
1308                 LogFatal("Cannot stat %s for size.", imakefile);
1309         buf = Emalloc((int)st.st_size+3);
1310         count = fread(buf + 2, 1, st.st_size, inFile);
1311         if (count == 0  &&  st.st_size != 0)
1312                 LogFatal("Cannot read %s:", imakefile);
1313         fclose(inFile);
1314         buf[0] = '\n';
1315         buf[1] = '\n';
1316         buf[count + 2] = '\0';
1317
1318         punwritten = pbuf = buf + 2;
1319         while (*pbuf) {
1320             /* for compatibility, replace make comments for cpp */
1321             if (*pbuf == '#' && pbuf[-1] == '\n' && pbuf[-2] != '\\') {
1322                 ptoken = pbuf+1;
1323                 while (*ptoken == ' ' || *ptoken == '\t')
1324                         ptoken++;
1325                 pend = ptoken;
1326                 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
1327                         pend++;
1328                 savec = *pend;
1329                 *pend = '\0';
1330                 if (strcmp(ptoken, "define") &&
1331                     strcmp(ptoken, "if") &&
1332                     strcmp(ptoken, "ifdef") &&
1333                     strcmp(ptoken, "ifndef") &&
1334                     strcmp(ptoken, "include") &&
1335                     strcmp(ptoken, "line") &&
1336                     strcmp(ptoken, "else") &&
1337                     strcmp(ptoken, "elif") &&
1338                     strcmp(ptoken, "endif") &&
1339                     strcmp(ptoken, "error") &&
1340                     strcmp(ptoken, "pragma") &&
1341                     strcmp(ptoken, "undef")) {
1342                     if (outFile == NULL) {
1343                         tmpImakefile = Strdup(tmpImakefile);
1344                         int ret = mkstemp(tmpImakefile);
1345                         (void) ret;
1346                         outFile = fopen(tmpImakefile, "w");
1347                         if (outFile == NULL)
1348                             LogFatal("Cannot open %s for write.",
1349                                 tmpImakefile);
1350                     }
1351                     writetmpfile(outFile, punwritten, pbuf-punwritten,
1352                                  tmpImakefile);
1353                     if (ptoken > pbuf + 1)
1354                         writetmpfile(outFile, "XCOMM", 5, tmpImakefile);
1355                     else
1356                         writetmpfile(outFile, "XCOMM ", 6, tmpImakefile);
1357                     punwritten = pbuf + 1;
1358                 }
1359                 *pend = savec;
1360             }
1361             pbuf++;
1362         }
1363         if (outFile) {
1364             writetmpfile(outFile, punwritten, pbuf-punwritten, tmpImakefile);
1365             fclose(outFile);
1366             return tmpImakefile;
1367         }
1368
1369         return(imakefile);
1370 }
1371
1372 static void
1373 CleanCppOutput(FILE *tmpfd, const char *tmpfname)
1374 {
1375         char    *input;
1376         int     blankline = 0;
1377
1378         while ((input = ReadLine(tmpfd, tmpfname))) {
1379                 if (isempty(input)) {
1380                         if (blankline++)
1381                                 continue;
1382                         KludgeResetRule();
1383                 } else {
1384                         blankline = 0;
1385                         KludgeOutputLine(&input);
1386                         writetmpfile(tmpfd, input, strlen(input), tmpfname);
1387                 }
1388                 writetmpfile(tmpfd, "\n", 1, tmpfname);
1389         }
1390         fflush(tmpfd);
1391 #ifdef NFS_STDOUT_BUG
1392         /*
1393          * On some systems, NFS seems to leave a large number of nulls at
1394          * the end of the file.  Ralph Swick says that this kludge makes the
1395          * problem go away.
1396          */
1397         ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
1398 #endif
1399 }
1400
1401 /*
1402  * Determine if a line has nothing in it.  As a side effect, we trim white
1403  * space from the end of the line.  Cpp magic cookies are also thrown away.
1404  * "XCOMM" token is transformed to "#".
1405  */
1406 static boolean
1407 isempty(char *line)
1408 {
1409         char    *pend;
1410
1411         /*
1412          * Check for lines of the form
1413          *      # n "...
1414          * or
1415          *      # line n "...
1416          */
1417         if (*line == '#') {
1418                 pend = line+1;
1419                 if (*pend == ' ')
1420                         pend++;
1421                 if (*pend == 'l' && pend[1] == 'i' && pend[2] == 'n' &&
1422                     pend[3] == 'e' && pend[4] == ' ')
1423                         pend += 5;
1424                 if (isdigit((int)*pend)) {
1425                         do {
1426                             pend++;
1427                         } while (isdigit((int)*pend));
1428                         if (*pend == '\n' || *pend == '\0')
1429                                 return(TRUE);
1430                         if (*pend++ == ' ' && *pend == '"')
1431                                 return(TRUE);
1432                 }
1433                 while (*pend)
1434                     pend++;
1435         } else {
1436             for (pend = line; *pend; pend++) {
1437                 if (*pend == 'X' && pend[1] == 'C' && pend[2] == 'O' &&
1438                     pend[3] == 'M' && pend[4] == 'M' &&
1439                     (pend == line || pend[-1] == ' ' || pend[-1] == '\t') &&
1440                     (pend[5] == ' ' || pend[5] == '\t' || pend[5] == '\0'))
1441                 {
1442                     *pend = '#';
1443                     strncpy(pend+1, pend+5, 1);
1444                 }
1445 #ifdef MAGIC_MAKE_VARS
1446                 if (*pend == 'X' && pend[1] == 'V' && pend[2] == 'A' &&
1447                     pend[3] == 'R')
1448                 {
1449                     char varbuf[5];
1450                     int i;
1451
1452                     if (pend[4] == 'd' && pend[5] == 'e' && pend[6] == 'f' &&
1453                         pend[7] >= '0' && pend[7] <= '9')
1454                     {
1455                         i = pend[7] - '0';
1456                         snprintf(varbuf, 5, "%0.4d", xvariable);
1457                         strncpy(pend+4, varbuf, 4);
1458                         xvariables[i] = xvariable;
1459                         xvariable = (xvariable + 1) % 10000;
1460                     }
1461                     else if (pend[4] == 'u' && pend[5] == 's' &&
1462                              pend[6] == 'e' && pend[7] >= '0' &&
1463                              pend[7] <= '9')
1464                     {
1465                         i = pend[7] - '0';
1466                         snprintf(varbuf, 5, "%0.4d", xvariables[i]);
1467                         strncpy(pend+4, varbuf, 4);
1468                     }
1469                 }
1470 #endif
1471             }
1472         }
1473         while (--pend >= line && (*pend == ' ' || *pend == '\t')) ;
1474         pend[1] = '\0';
1475         return (*line == '\0');
1476 }
1477
1478 /*ARGSUSED*/
1479 static char *
1480 ReadLine(FILE *tmpfd, const char *tmpfname)
1481 {
1482         static boolean  initialized = FALSE;
1483         static char     *buf, *pline, *end;
1484         char    *p1, *p2;
1485
1486         if (! initialized) {
1487 #ifdef WIN32
1488                 FILE *fp = tmpfd;
1489 #endif
1490                 int     total_red;
1491                 struct stat     st;
1492
1493                 /*
1494                  * Slurp it all up.
1495                  */
1496                 fseek(tmpfd, 0, 0);
1497                 if (fstat(fileno(tmpfd), &st) < 0)
1498                         LogFatal("cannot stat %s for size", tmpMakefile);
1499                 pline = buf = Emalloc((int)st.st_size+1);
1500                 total_red = fread(buf, 1, st.st_size, tmpfd);
1501                 if (total_red == 0  &&  st.st_size != 0)
1502                         LogFatal("cannot read %s", tmpMakefile);
1503                 end = buf + total_red;
1504                 *end = '\0';
1505                 fseek(tmpfd, 0, 0);
1506 #if defined(SYSV) || defined(WIN32)
1507                 tmpfd = freopen(tmpfname, "w+", tmpfd);
1508 #ifdef WIN32
1509                 if (! tmpfd) /* if failed try again */
1510                         tmpfd = freopen(tmpfname, "w+", fp);
1511 #endif
1512                 if (! tmpfd)
1513                         LogFatal("cannot reopen %s.", tmpfname);
1514 #else   /* !SYSV */
1515                 int ret = ftruncate(fileno(tmpfd), (off_t) 0);
1516                 (void) ret;
1517 #endif  /* !SYSV */
1518                 initialized = TRUE;
1519             fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
1520             fprintf (tmpfd, "# %s\n",
1521                 "$TOG: imake.c /main/104 1998/03/24 12:45:15 kaleb $");
1522         }
1523
1524         for (p1 = pline; p1 < end; p1++) {
1525                 if (*p1 == '@' && *(p1+1) == '@'
1526                     /* ignore ClearCase version-extended pathnames */
1527                     && !(p1 != pline && !isspace((int)*(p1-1))
1528                     && *(p1+2) == '/'))
1529                 { /* soft EOL */
1530                         *p1++ = '\0';
1531                         p1++; /* skip over second @ */
1532                         break;
1533                 }
1534                 else if (*p1 == '\n') { /* real EOL */
1535 #ifdef WIN32
1536                         if (p1 > pline && p1[-1] == '\r')
1537                                 p1[-1] = '\0';
1538 #endif
1539                         *p1++ = '\0';
1540                         break;
1541                 }
1542         }
1543
1544         /*
1545          * return NULL at the end of the file.
1546          */
1547         p2 = (pline == p1 ? NULL : pline);
1548         pline = p1;
1549         return(p2);
1550 }
1551
1552 static void
1553 writetmpfile(FILE *fd, const char *buf, int cnt, const char *fname)
1554 {
1555         if (fwrite(buf, sizeof(char), cnt, fd) == -1)
1556                 LogFatal("Cannot write to %s.", fname);
1557 }
1558
1559 static char *
1560 Emalloc(int size)
1561 {
1562         char    *p;
1563
1564         if ((p = malloc(size)) == NULL)
1565                 LogFatal("Cannot allocate %d bytes.", size);
1566         return(p);
1567 }
1568
1569 #ifdef FIXUP_CPP_WHITESPACE
1570 static void
1571 KludgeOutputLine(char **pline)
1572 {
1573         char    *p = *pline;
1574         char    quotechar = '\0';
1575
1576         switch (*p) {
1577             case '#':   /*Comment - ignore*/
1578                 break;
1579             case '\t':  /*Already tabbed - ignore it*/
1580                 break;
1581             case ' ':   /*May need a tab*/
1582             default:
1583 # ifdef INLINE_SYNTAX
1584                 if (*p == '<' && p[1] == '<') { /* inline file close */
1585                     InInline--;
1586                     InRule = TRUE;
1587                     break;
1588                 }
1589 # endif
1590                 /*
1591                  * The following cases should not be treated as beginning of 
1592                  * rules:
1593                  * variable := name     (GNU make)
1594                  * variable = .*:.*     (':' should be allowed as value)
1595                  *      sed 's:/a:/b:'  (: used in quoted values)
1596                  */
1597                 for (; *p; p++) {
1598                     if (quotechar) {
1599                         if (quotechar == '\\' ||
1600                             (*p == quotechar &&
1601 # ifdef WIN32
1602                              quotechar != ')' &&
1603 # endif
1604                              p[-1] != '\\'))
1605                             quotechar = '\0';
1606                         continue;
1607                     }
1608                     switch (*p) {
1609                     case '\\':
1610                     case '"':
1611                     case '\'':
1612                         quotechar = *p;
1613                         break;
1614                     case '(':
1615                         quotechar = ')';
1616                         break;
1617                     case '{':
1618                         quotechar = '}';
1619                         break;
1620                     case '[':
1621                         quotechar = ']';
1622                         break;
1623                     case '=':
1624 # ifdef REMOVE_CPP_LEADSPACE
1625                         if (!InRule && **pline == ' ') {
1626                             while (**pline == ' ')
1627                                 (*pline)++;
1628                         }
1629 # endif
1630                         goto breakfor;
1631 # ifdef INLINE_SYNTAX
1632                     case '<':
1633                         if (p[1] == '<') /* inline file start */
1634                             InInline++;
1635                         break;
1636 # endif
1637                     case ':':
1638                         if (p[1] == '=')
1639                             goto breakfor;
1640                         while (**pline == ' ')
1641                             (*pline)++;
1642                         InRule = TRUE;
1643                         return;
1644                     }
1645                 }
1646 breakfor:
1647                 if (InRule && **pline == ' ')
1648                     **pline = '\t';
1649                 break;
1650         }
1651 }
1652
1653 static void
1654 KludgeResetRule(void)
1655 {
1656         InRule = FALSE;
1657 }
1658 #endif /* FIXUP_CPP_WHITESPACE */
1659
1660 static char *
1661 Strdup(const char *cp)
1662 {
1663         char *new = Emalloc(strlen(cp) + 1);
1664
1665         strncpy(new, cp, strlen(cp) + 1);
1666         return new;
1667 }