5e26133a42088605b457428327b0735e569043e2
[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                 mkstemp(tmpMakefile);
409         }
410         AddMakeArg("-f");
411         AddMakeArg( tmpMakefile );
412         sprintf(makeMacro, "MAKE=%s", program);
413         AddMakeArg( makeMacro );
414         sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
415         AddMakeArg( makefileMacro );
416
417         if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
418                 LogFatal("Cannot create temporary file %s.", tmpMakefile);
419
420         cleanedImakefile = CleanCppInput(Imakefile);
421         cppit(cleanedImakefile, Template, ImakefileC, tmpfd, tmpMakefile);
422
423         if (show) {
424                 if (Makefile == NULL)
425                         showit(tmpfd);
426         } else
427                 makeit();
428         wrapup();
429         return 0;
430 }
431
432 static void
433 showit(FILE *fd)
434 {
435         char    buf[ BUFSIZ ];
436         int     red;
437
438         fseek(fd, 0, 0);
439         while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
440                 writetmpfile(stdout, buf, red, "stdout");
441         if (red < 0)
442             LogFatal("Cannot read %s.", tmpMakefile);
443 }
444
445 static void
446 wrapup(void)
447 {
448         if (tmpMakefile != Makefile)
449                 unlink(tmpMakefile);
450         if (cleanedImakefile && cleanedImakefile != Imakefile)
451                 unlink(cleanedImakefile);
452         if (haveImakefileC)
453                 unlink(ImakefileC);
454 }
455
456 static
457 #ifdef SIGNALRETURNSINT
458 int
459 #else
460 void
461 #endif
462 catch(int sig)
463 {
464         errno = 0;
465         LogFatal("Signal %d.", sig);
466 }
467
468 /*
469  * Initialize some variables.
470  */
471 static void
472 init(void)
473 {
474         char    *p;
475
476         make_argindex=0;
477         while (make_argv[ make_argindex ] != NULL)
478                 make_argindex++;
479         cpp_argindex = 0;
480         while (cpp_argv[ cpp_argindex ] != NULL)
481                 cpp_argindex++;
482
483         /*
484          * See if the standard include directory is different than
485          * the default.  Or if cpp is not the default.  Or if the make
486          * found by the PATH variable is not the default.
487          */
488         if ((p = getenv("IMAKEINCLUDE"))) {
489                 if (*p != '-' || *(p+1) != 'I')
490                         LogFatal("Environment var IMAKEINCLUDE %s must begin with -I");
491                 AddCppArg(p);
492                 for (; *p; p++)
493                         if (*p == ' ') {
494                                 *p++ = '\0';
495                                 AddCppArg(p);
496                         }
497         }
498         if ((p = getenv("IMAKECPP")))
499                 cpp = p;
500         if ((p = getenv("IMAKEMAKE")))
501                 make_argv[0] = p;
502
503         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
504                 signal(SIGINT, catch);
505 }
506
507 static void
508 AddMakeArg(char *arg)
509 {
510         errno = 0;
511         if (make_argindex >= ARGUMENTS-1)
512                 LogFatal("Out of internal storage.");
513         make_argv[ make_argindex++ ] = arg;
514         make_argv[ make_argindex ] = NULL;
515 }
516
517 static void
518 AddCppArg(char *arg)
519 {
520         errno = 0;
521         if (cpp_argindex >= ARGUMENTS-1)
522                 LogFatal("Out of internal storage.");
523         cpp_argv[ cpp_argindex++ ] = arg;
524         cpp_argv[ cpp_argindex ] = NULL;
525 }
526
527 static void
528 SetOpts(int argc, char **argv)
529 {
530         errno = 0;
531         /*
532          * Now gather the arguments for make
533          */
534         for(argc--, argv++; argc; argc--, argv++) {
535             /*
536              * We intercept these flags.
537              */
538             if (argv[0][0] == '-') {
539                 if (argv[0][1] == 'D') {
540                     AddCppArg(argv[0]);
541                 } else if (argv[0][1] == 'I') {
542                     AddCppArg(argv[0]);
543                 } else if (argv[0][1] == 'f') {
544                     if (argv[0][2])
545                         Imakefile = argv[0]+2;
546                     else {
547                         argc--, argv++;
548                         if (! argc)
549                             LogFatal("No description arg after -f flag");
550                         Imakefile = argv[0];
551                     }
552                 } else if (argv[0][1] == 's') {
553                     if (argv[0][2])
554                         Makefile = ((argv[0][2] == '-') && !argv[0][3]) ?
555                             NULL : argv[0]+2;
556                     else {
557                         argc--, argv++;
558                         if (!argc)
559                             LogFatal("No description arg after -s flag");
560                         Makefile = ((argv[0][0] == '-') && !argv[0][1]) ?
561                             NULL : argv[0];
562                     }
563                     show = TRUE;
564                 } else if (argv[0][1] == 'e') {
565                    Makefile = (argv[0][2] ? argv[0]+2 : NULL);
566                    show = FALSE;
567                 } else if (argv[0][1] == 'T') {
568                     if (argv[0][2])
569                         Template = argv[0]+2;
570                     else {
571                         argc--, argv++;
572                         if (! argc)
573                             LogFatal("No description arg after -T flag");
574                         Template = argv[0];
575                     }
576                 } else if (argv[0][1] == 'C') {
577                     if (argv[0][2])
578                         ImakefileC = argv[0]+2;
579                     else {
580                         argc--, argv++;
581                         if (! argc)
582                             LogFatal("No imakeCfile arg after -C flag");
583                         ImakefileC = argv[0];
584                     }
585                 } else if (argv[0][1] == 'v') {
586                     verbose = TRUE;
587                 } else
588                     AddMakeArg(argv[0]);
589             } else
590                 AddMakeArg(argv[0]);
591         }
592 #ifdef USE_CC_E
593         if (!cpp)
594         {
595                 AddCppArg("-E");
596                 cpp = DEFAULT_CC;
597         }
598 #else
599         if (!cpp)
600                 cpp = DEFAULT_CPP;
601 #endif
602         cpp_argv[0] = cpp;
603         AddCppArg(ImakefileC);
604 }
605
606 static char *
607 FindImakefile(char *Imakefile)
608 {
609         if (Imakefile) {
610                 if (access(Imakefile, R_OK) < 0)
611                         LogFatal("Cannot find %s.", Imakefile);
612         } else {
613                 if (access("Imakefile", R_OK) < 0)
614                         if (access("imakefile", R_OK) < 0)
615                                 LogFatal("No description file.");
616                         else
617                                 Imakefile = "imakefile";
618                 else
619                         Imakefile = "Imakefile";
620         }
621         return(Imakefile);
622 }
623
624 static void
625 LogFatal(const char *s, ...)
626 {
627     static boolean entered = FALSE;
628     va_list args;
629
630     if (entered)
631         return;
632
633     entered = TRUE;
634
635     va_start(args, s);
636     Log(s, args);
637     va_end(args);
638
639     fprintf(stderr, "Stop.\n");
640
641     wrapup();
642
643     exit(1);
644 }
645
646 static void
647 LogMsg(const char *s, ...)
648 {
649     va_list args;
650
651     va_start(args, s);
652     Log(s, args);
653     va_end(args);
654 }
655
656 static void
657 Log(const char *s, va_list args)
658 {
659     int error_number = errno;
660
661     if (error_number) {
662         fprintf(stderr, "%s: ", program);
663         fprintf(stderr, "%s\n", strerror(error_number));
664     }
665     fprintf(stderr, "%s: ", program);
666     vfprintf(stderr, s, args);
667     fprintf(stderr, "\n");
668 }
669
670 static void
671 showargs(char **argv)
672 {
673         for (; *argv; argv++)
674                 fprintf(stderr, "%s ", *argv);
675         fprintf(stderr, "\n");
676 }
677
678 #define ImakefileCHeader "/* imake - temporary file */"
679
680 static void
681 CheckImakefileC(const char *masterc)
682 {
683         char mkcbuf[1024];
684         FILE *inFile;
685
686         if (access(masterc, F_OK) == 0) {
687                 inFile = fopen(masterc, "r");
688                 if (inFile == NULL)
689                         LogFatal("Refuse to overwrite: %s", masterc);
690                 if ((fgets(mkcbuf, sizeof(mkcbuf), inFile) &&
691                      strncmp(mkcbuf, ImakefileCHeader, 
692                              sizeof(ImakefileCHeader)-1)))
693                 {
694                         fclose(inFile);
695                         LogFatal("Refuse to overwrite: %s", masterc);
696                 }
697                 fclose(inFile);
698         }
699 }
700
701 #define LocalDefineFmt  "#define %s \"%s\"\n"
702 #define IncludeFmt      "#include %s\n"
703 #define ImakeDefSym     "INCLUDE_IMAKEFILE"
704 #define ImakeTmplSym    "IMAKE_TEMPLATE"
705 #define OverrideWarning "Warning: local file \"%s\" overrides global macros."
706
707 static boolean
708 optional_include(FILE *inFile, const char *defsym, const char *fname)
709 {
710         errno = 0;
711         if (access(fname, R_OK) == 0) {
712                 LogMsg(OverrideWarning, fname);
713                 return (fprintf(inFile, LocalDefineFmt, defsym, fname) < 0 ||
714                         fprintf(inFile, IncludeFmt, defsym) < 0);
715         }
716         return FALSE;
717 }
718
719 static void
720 doit(FILE *outfd, const char *cmd, char **argv)
721 {
722         int     pid;
723         waitType        status;
724
725         /*
726          * Fork and exec the command.
727          */
728 #ifdef WIN32
729         if (outfd)
730                 dup2(fileno(outfd), 1);
731         status = _spawnvp(_P_WAIT, cmd, argv);
732         if (status < 0)
733                 LogFatal("Cannot spawn %s.", cmd);
734         if (status > 0)
735                 LogFatal("Exit code %d.", status);
736 #else
737         pid = fork();
738         if (pid < 0)
739                 LogFatal("Cannot fork.");
740         if (pid) {      /* parent... simply wait */
741                 while (wait(&status) > 0) {
742                         errno = 0;
743                         if (WIFSIGNALED(status))
744                                 LogFatal("Signal %d.", waitSig(status));
745                         if (WIFEXITED(status) && waitCode(status))
746                                 LogFatal("Exit code %d.", waitCode(status));
747                 }
748         }
749         else {  /* child... dup and exec cmd */
750                 if (verbose)
751                         showargs(argv);
752                 if (outfd)
753                         dup2(fileno(outfd), 1);
754                 execvp(cmd, argv);
755                 LogFatal("Cannot exec %s.", cmd);
756         }
757 #endif
758 }
759
760 #ifndef WIN32
761
762 #if (defined(DEFAULT_OS_NAME) || defined(DEFAULT_OS_MAJOR_REV) || \
763      defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
764 static void
765 parse_utsname(struct utsname *name, const char *fmt, char *result, const char *msg)
766 {
767   char buf[SYS_NMLN * 5 + 1];
768   char *ptr = buf;
769   int arg;
770
771   /* Assemble all the pieces into a buffer. */
772   for (arg = 0; fmt[arg] != ' '; arg++)
773     {
774       /* Our buffer is only guaranteed to hold 5 arguments. */
775       if (arg >= 5)
776         LogFatal(msg, fmt);
777
778       switch (fmt[arg])
779         {
780         case 's':
781           if (arg > 0)
782             *ptr++ = ' ';
783           strcpy(ptr, name->sysname);
784           ptr += strlen(ptr);
785           break;
786
787         case 'n':
788           if (arg > 0)
789             *ptr++ = ' ';
790           strcpy(ptr, name->nodename);
791           ptr += strlen(ptr);
792           break;
793
794         case 'r':
795           if (arg > 0)
796             *ptr++ = ' ';
797           strcpy(ptr, name->release);
798           ptr += strlen(ptr);
799           break;
800
801         case 'v':
802           if (arg > 0)
803             *ptr++ = ' ';
804           strcpy(ptr, name->version);
805           ptr += strlen(ptr);
806           break;
807
808         case 'm':
809           if (arg > 0)
810             *ptr++ = ' ';
811           strcpy(ptr, name->machine);
812           ptr += strlen(ptr);
813           break;
814
815         default:
816           LogFatal(msg, fmt);
817         }
818     }
819
820   /* Just in case... */
821   if (strlen(buf) >= sizeof(buf))
822     LogFatal("Buffer overflow parsing uname.");
823
824   /* Parse the buffer.  The sscanf() return value is rarely correct. */
825   *result = '\0';
826   (void) sscanf(buf, fmt + arg + 1, result);
827 }
828 #endif
829
830 /* Trim leading 0's and periods from version names.  The 0's cause
831    the number to be interpreted as octal numbers.  Some version strings
832    have the potential for different numbers of .'s in them.
833  */
834         
835 #if (defined(DEFAULT_OS_MAJOR_REV) || defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
836 static const char *
837 trim_version(const char *p)
838 {
839
840         if (p != 0 && *p != '\0')
841         {
842                 while ((*p == '0' || *p == '.') && *(p + 1) != '\0')
843                         ++p;
844         }
845         return (p);
846 }
847 #endif
848
849 #endif
850
851 #ifdef linux
852 static void
853 get_distrib(FILE *inFile)
854 {
855   struct stat sb;
856
857   static char* yast = "/sbin/YaST";
858   static char* redhat = "/etc/redhat-release";
859
860   fprintf (inFile, "%s\n", "#define LinuxUnknown    0");
861   fprintf (inFile, "%s\n", "#define LinuxSuSE       1");
862   fprintf (inFile, "%s\n", "#define LinuxCaldera    2");
863   fprintf (inFile, "%s\n", "#define LinuxCraftworks 3");
864   fprintf (inFile, "%s\n", "#define LinuxDebian     4");
865   fprintf (inFile, "%s\n", "#define LinuxInfoMagic  5");
866   fprintf (inFile, "%s\n", "#define LinuxKheops     6");
867   fprintf (inFile, "%s\n", "#define LinuxPro        7");
868   fprintf (inFile, "%s\n", "#define LinuxRedHat     8");
869   fprintf (inFile, "%s\n", "#define LinuxSlackware  9");
870   fprintf (inFile, "%s\n", "#define LinuxTurbo      10");
871   fprintf (inFile, "%s\n", "#define LinuxWare       11");
872   fprintf (inFile, "%s\n", "#define LinuxYggdrasil  12");
873
874   if (lstat (yast, &sb) == 0) {
875     fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxSuSE");
876     return;
877   }
878   if (lstat (redhat, &sb) == 0) {
879     fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxRedHat");
880     return;
881   }
882   /* what's the definitive way to tell what any particular distribution is? */
883
884   fprintf (inFile, "%s\n", "#define DefaultLinuxDistribution LinuxUnknown");
885   /* would like to know what version of the distribution it is */
886 }
887
888 static const char libc_c[]=
889 "#include <stdio.h>\n"
890 "#include <ctype.h>\n"
891 "\n"
892 "#if 0\n"
893 "#pragma weak gnu_get_libc_version\n"
894 "#pragma weak __libc_version\n"
895 "#pragma weak __linux_C_lib_version\n"
896 "#else\n"
897 "asm (\".weak gnu_get_libc_version\");\n"
898 "asm (\".weak __libc_version\");\n"
899 "asm (\".weak __linux_C_lib_version\");\n"
900 "#endif\n"
901 "\n"
902 "extern const char * gnu_get_libc_version (void);\n"
903 "extern const char * __linux_C_lib_version;\n"
904 "extern const char __libc_version [];\n"
905 "\n"
906 "int\n"
907 "main ()\n"
908 "{\n"
909 "  int libcmajor = 0, libcminor = 0, libcteeny = 0;\n"
910 "\n"
911 "  if (((&__linux_C_lib_version != 0)\n"
912 "       && ((&__libc_version != 0) || (gnu_get_libc_version != 0)))\n"
913 "      || (!(&__linux_C_lib_version != 0) && !(&__libc_version != 0)\n"
914 "         && !(gnu_get_libc_version != 0)))\n"
915 "  {\n"
916 "    libcmajor = 0;\n"
917 "    libcminor = 0;\n"
918 "    libcteeny = 0;\n"
919 "  }\n"
920 "  else\n"
921 "  {\n"
922 "    const char * ptr;\n"
923 "    int glibcmajor = 0;\n"
924 "\n"
925 "    if (gnu_get_libc_version != 0)\n"
926 "    {\n"
927 "      ptr = gnu_get_libc_version ();\n"
928 "      glibcmajor = 4;\n"
929 "    }\n"
930 "    else if (&__libc_version != 0)\n"
931 "    {\n"
932 "      ptr = __libc_version;\n"
933 "      glibcmajor = 4;\n"
934 "    }\n"
935 "    else\n"
936 "      ptr = __linux_C_lib_version;\n"
937 "\n"
938 "    while (!isdigit (*ptr))\n"
939 "      ptr++;\n"
940 "\n"
941 "    sscanf (ptr, \"%d.%d.%d\", &libcmajor, &libcminor, &libcteeny);\n"
942 "    libcmajor += glibcmajor;\n"
943 "  }\n"
944 "\n"
945 "  printf(\"#define DefaultLinuxCLibMajorVersion %d\\n\", libcmajor);\n"
946 "  printf(\"#define DefaultLinuxCLibMinorVersion %d\\n\", libcminor);\n"
947 "  printf(\"#define DefaultLinuxCLibTeenyVersion %d\\n\", libcteeny);\n"
948 "\n"
949 "  return 0;\n"
950 "}\n"
951 ;
952
953 static void
954 get_libc_version(FILE *inFile)
955 {
956   char* libcso = NULL;
957   struct stat sb;
958   char buf[PATH_MAX];
959   char* ptr;
960   int libcmajor, libcminor, libcteeny;
961   struct utsname u;
962
963   /*
964    * If we are on a 64-bit Linux system and we see /usr/lib64/libc.so,
965    * we should use it.  Otherwise go with /usr/lib/libc.so.  It is entirely
966    * possible that someone will be running a 32-bit userland on a 64-bit
967    * system.
968    */
969   if (uname(&u) == -1) {
970     fprintf(stderr, "%s (%d): %s\n", __func__, __LINE__, strerror(errno));
971     abort();
972   }
973
974   if (!strcmp(u.sysname, "Linux") &&
975       (!strcmp(u.machine, "x86_64"))) {
976     if (!lstat ("/usr/lib64/libc.so", &sb) && S_ISREG(sb.st_mode)) {
977       libcso = strdup("/usr/lib64/libc.so");
978     }
979   }
980
981   if (libcso == NULL) {
982     libcso = strdup("/usr/lib/libc.so");
983   }
984
985   if (lstat (libcso, &sb) == 0) {
986     if (S_ISLNK (sb.st_mode)) {
987       /* 
988        * /usr/lib/libc.so is a symlink -- this is libc 5.x
989        * we can do this the quick way
990         */
991       if (readlink (libcso, buf, PATH_MAX) >= 0) {
992         for (ptr = buf; *ptr && !isdigit (*ptr); ptr++);
993           (void) sscanf (ptr, "%d.%d.%d", &libcmajor, &libcminor, &libcteeny);
994           fprintf(inFile, "#define DefaultLinuxCLibMajorVersion %d\n", libcmajor);    
995           fprintf(inFile, "#define DefaultLinuxCLibMinorVersion %d\n", libcminor);    
996           fprintf(inFile, "#define DefaultLinuxCLibTeenyVersion %d\n", libcteeny);    
997       }
998     } else {
999       /* 
1000        * /usr/lib/libc.so is NOT a symlink -- this is libc 6.x / glibc 2.x
1001        * now we have to figure this out the hard way.
1002        */
1003       char *aout = tmpnam (NULL);
1004       FILE *fp;
1005       const char *format = "%s -o %s -x c -";
1006       char *cc;
1007       int len;
1008       char *command;
1009
1010       cc = getenv ("CC");
1011       if (cc == NULL)
1012         cc = "gcc";
1013       len = strlen (aout) + strlen (format) + strlen (cc);
1014       if (len < 128) len = 128;
1015       command = alloca (len);
1016
1017       if (snprintf (command , len, format, cc, aout) == len)
1018         abort ();
1019
1020       fp = popen (command, "w");
1021       if (fp == NULL || fprintf (fp, "%s\n", libc_c) < 0 || pclose (fp) != 0)
1022         abort ();
1023
1024       fp = popen (aout, "r");
1025       if (fp == NULL)
1026         abort ();
1027
1028       while (fgets (command, len, fp))
1029         fprintf (inFile, command);
1030   
1031       len = pclose (fp);
1032       remove (aout);
1033       if (len)
1034         abort ();
1035     }
1036   }
1037
1038   free(libcso);
1039 }
1040
1041 static void
1042 get_ld_version(FILE *inFile)
1043 {
1044   FILE* ldprog = popen ("ld -v", "r");
1045   char c;
1046   int ldmajor, ldminor;
1047
1048   if (ldprog) {
1049     do {
1050       c = fgetc (ldprog);
1051     } while (c != EOF && !isdigit (c));
1052     ungetc (c, ldprog);
1053     (void) fscanf (ldprog, "%d.%d", &ldmajor, &ldminor);
1054     fprintf(inFile, "#define DefaultLinuxBinUtilsMajorVersion %d\n", 
1055             ldmajor * 10 + ldminor);    
1056     pclose (ldprog);
1057   }
1058 }
1059 #endif
1060
1061 #ifndef PATH_MAX
1062 #define PATH_MAX 1024
1063 #endif
1064
1065 #if defined(sun) && defined(__SVR4)
1066 static void
1067 get_sun_compiler_versions(FILE *inFile)
1068 {
1069   char buf[PATH_MAX];
1070   char cmd[PATH_MAX];
1071   static char* sunpro_cc = "/opt/SUNWspro/bin/cc";
1072   static char* sunpro_CC = "/opt/SUNWspro/bin/CC";
1073   int cmajor, cminor;
1074   char* vptr;
1075   struct stat sb;
1076   FILE* ccproc;
1077
1078   if (lstat (sunpro_cc, &sb) == 0) {
1079     strcpy (cmd, sunpro_cc);
1080     strcat (cmd, " -V 2>&1");
1081     if ((ccproc = popen (cmd, "r")) != NULL) {
1082       if (fgets (buf, PATH_MAX, ccproc) != NULL) {
1083         vptr = strrchr (buf, 'C');
1084         for (; !isdigit(*vptr); vptr++);
1085         (void) sscanf (vptr, "%d.%d", &cmajor, &cminor);
1086         fprintf (inFile, 
1087                  "#define DefaultSunProCCompilerMajorVersion %d\n",
1088                  cmajor);
1089         fprintf (inFile, 
1090                  "#define DefaultSunProCCompilerMinorVersion %d\n",
1091                  cminor);
1092       }
1093       while (fgets (buf, PATH_MAX, ccproc) != NULL) {};
1094       pclose (ccproc);
1095     }
1096   }
1097   if (lstat (sunpro_CC, &sb) == 0) {
1098     strcpy (cmd, sunpro_CC);
1099     strcat (cmd, " -V 2>&1");
1100     if ((ccproc = popen (cmd, "r")) != NULL) {
1101       if (fgets (buf, PATH_MAX, ccproc) != NULL) {
1102         vptr = strrchr (buf, 'C');
1103         for (; !isdigit(*vptr); vptr++);
1104         (void) sscanf (vptr, "%d.%d", &cmajor, &cminor);
1105         fprintf (inFile, 
1106                  "#define DefaultSunProCplusplusCompilerMajorVersion %d\n",
1107                  cmajor);
1108         fprintf (inFile, 
1109                  "#define DefaultSunProCplusplusCompilerMinorVersion %d\n",
1110                  cminor);
1111       }
1112       while (fgets (buf, PATH_MAX, ccproc) != NULL) {};
1113       pclose (ccproc);
1114     }
1115   }
1116 }
1117 #endif
1118
1119 static void
1120 get_gcc_incdir(FILE *inFile)
1121 {
1122   static char* gcc_path[] = {
1123 #ifdef linux
1124     "/usr/bin/cc",      /* for Linux PostIncDir */
1125 #endif
1126     "/usr/local/bin/gcc",
1127     "/opt/gnu/bin/gcc"
1128   };
1129   struct stat sb;
1130   int i;
1131   FILE* gccproc;
1132   char buf[PATH_MAX];
1133   char cmd[PATH_MAX];
1134   char* ptr;
1135
1136   buf[0] = '\0';
1137   for (i = 0; i < sizeof gcc_path / sizeof gcc_path[0]; i++) {
1138     if (lstat (gcc_path[i], &sb) == 0) {
1139       strcpy (cmd, gcc_path[i]);
1140       strcat (cmd, " --print-libgcc-file-name");
1141       if ((gccproc = popen (cmd, "r")) != NULL) {
1142         if (fgets (buf, PATH_MAX, gccproc) != NULL) {
1143           ptr = strstr (buf, "libgcc.a");
1144           if (ptr) strcpy (ptr, "include");
1145         }
1146         (void) pclose (gccproc);
1147         break;
1148       }
1149     }
1150   }
1151   if (buf[0])
1152     fprintf (inFile, "#define DefaultGccIncludeDir %s\n", buf);
1153 }
1154
1155 static boolean
1156 define_os_defaults(FILE *inFile)
1157 {
1158 #ifndef WIN32
1159 #if (defined(DEFAULT_OS_NAME) || defined(DEFAULT_OS_MAJOR_REV) || \
1160      defined(DEFAULT_OS_MINOR_REV) || defined(DEFAULT_OS_TEENY_REV))
1161         struct utsname name;
1162         char buf[SYS_NMLN * 5 + 1];
1163
1164         /* Obtain the system information. */
1165         if (uname(&name) < 0)
1166                 LogFatal("Cannot invoke uname");
1167
1168 # ifdef DEFAULT_OS_NAME
1169         parse_utsname(&name, DEFAULT_OS_NAME, buf, 
1170                       "Bad DEFAULT_OS_NAME syntax %s");
1171         if (buf[0] != '\0')
1172                 fprintf(inFile, "#define DefaultOSName %s\n", buf);
1173 # endif
1174
1175 # ifdef DEFAULT_OS_MAJOR_REV
1176         parse_utsname(&name, DEFAULT_OS_MAJOR_REV, buf,
1177                       "Bad DEFAULT_OS_MAJOR_REV syntax %s");
1178         fprintf(inFile, "#define DefaultOSMajorVersion %s\n", 
1179                 *buf ? trim_version(buf) : "0");
1180 # endif
1181
1182 # ifdef DEFAULT_OS_MINOR_REV
1183         parse_utsname(&name, DEFAULT_OS_MINOR_REV, buf,
1184                       "Bad DEFAULT_OS_MINOR_REV syntax %s");
1185         fprintf(inFile, "#define DefaultOSMinorVersion %s\n", 
1186                 *buf ? trim_version(buf) : "0");
1187 # endif
1188
1189 # ifdef DEFAULT_OS_TEENY_REV
1190         parse_utsname(&name, DEFAULT_OS_TEENY_REV, buf,
1191                       "Bad DEFAULT_OS_TEENY_REV syntax %s");
1192         fprintf(inFile, "#define DefaultOSTeenyVersion %s\n", 
1193                 *buf ? trim_version(buf) : "0");
1194 # endif
1195 #endif
1196 #ifdef linux
1197     get_distrib (inFile);
1198     get_libc_version (inFile);
1199     get_ld_version(inFile);
1200 #endif
1201     get_gcc_incdir(inFile);
1202 #if defined (sun) && defined(SVR4)
1203     get_sun_compiler_versions (inFile);
1204 #endif
1205 #else /* WIN32 */
1206    OSVERSIONINFO osvi;
1207    static char* os_names[] = { "Win32s", "Windows 95", "Windows NT" };
1208
1209    memset(&osvi, 0, sizeof(OSVERSIONINFO));
1210    osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
1211    GetVersionEx (&osvi);
1212
1213    fprintf (inFile, "#define DefaultOSName Microsoft %s\n", 
1214             os_names[osvi.dwPlatformId]);
1215
1216    fprintf(inFile, "#define DefaultOSMajorVersion %d\n", osvi.dwMajorVersion);
1217    fprintf(inFile, "#define DefaultOSMinorVersion %d\n", osvi.dwMinorVersion);
1218    fprintf(inFile, "#define DefaultOSTeenyVersion %d\n", 
1219            osvi.dwBuildNumber & 0xFFFF);
1220 #endif /* WIN32 */
1221    return FALSE;
1222 }
1223
1224 static void
1225 cppit(const char *imakefile, const char *template, const char *masterc, FILE *outfd, const char *outfname)
1226 {
1227         FILE    *inFile;
1228
1229         haveImakefileC = TRUE;
1230         inFile = fopen(masterc, "w");
1231         if (inFile == NULL)
1232                 LogFatal("Cannot open %s for output.", masterc);
1233         if (fprintf(inFile, "%s\n", ImakefileCHeader) < 0 ||
1234             define_os_defaults(inFile) ||
1235             optional_include(inFile, "IMAKE_LOCAL_DEFINES", "localdefines") ||
1236             optional_include(inFile, "IMAKE_ADMIN_DEFINES", "admindefines") ||
1237             fprintf(inFile, "#define %s <%s>\n", ImakeDefSym, imakefile) < 0 ||
1238             fprintf(inFile, LocalDefineFmt, ImakeTmplSym, template) < 0 ||
1239             fprintf(inFile, IncludeFmt, ImakeTmplSym) < 0 ||
1240             optional_include(inFile, "IMAKE_ADMIN_MACROS", "adminmacros") ||
1241             optional_include(inFile, "IMAKE_LOCAL_MACROS", "localmacros") ||
1242             fflush(inFile) || 
1243             fclose(inFile))
1244                 LogFatal("Cannot write to %s.", masterc);
1245         /*
1246          * Fork and exec cpp
1247          */
1248         doit(outfd, cpp, cpp_argv);
1249         CleanCppOutput(outfd, outfname);
1250 }
1251
1252 static void
1253 makeit(void)
1254 {
1255         doit(NULL, make_argv[0], make_argv);
1256 }
1257
1258 static char *
1259 CleanCppInput(char *imakefile)
1260 {
1261         FILE    *outFile = NULL;
1262         FILE    *inFile;
1263         char    *buf,           /* buffer for file content */
1264                 *pbuf,          /* walking pointer to buf */
1265                 *punwritten,    /* pointer to unwritten portion of buf */
1266                 *ptoken,        /* pointer to # token */
1267                 *pend,          /* pointer to end of # token */
1268                 savec;          /* temporary character holder */
1269         int     count;
1270         struct stat     st;
1271
1272         /*
1273          * grab the entire file.
1274          */
1275         if (!(inFile = fopen(imakefile, "r")))
1276                 LogFatal("Cannot open %s for input.", imakefile);
1277         if (fstat(fileno(inFile), &st) < 0)
1278                 LogFatal("Cannot stat %s for size.", imakefile);
1279         buf = Emalloc((int)st.st_size+3);
1280         count = fread(buf + 2, 1, st.st_size, inFile);
1281         if (count == 0  &&  st.st_size != 0)
1282                 LogFatal("Cannot read %s:", imakefile);
1283         fclose(inFile);
1284         buf[0] = '\n';
1285         buf[1] = '\n';
1286         buf[count + 2] = '\0';
1287
1288         punwritten = pbuf = buf + 2;
1289         while (*pbuf) {
1290             /* for compatibility, replace make comments for cpp */
1291             if (*pbuf == '#' && pbuf[-1] == '\n' && pbuf[-2] != '\\') {
1292                 ptoken = pbuf+1;
1293                 while (*ptoken == ' ' || *ptoken == '\t')
1294                         ptoken++;
1295                 pend = ptoken;
1296                 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
1297                         pend++;
1298                 savec = *pend;
1299                 *pend = '\0';
1300                 if (strcmp(ptoken, "define") &&
1301                     strcmp(ptoken, "if") &&
1302                     strcmp(ptoken, "ifdef") &&
1303                     strcmp(ptoken, "ifndef") &&
1304                     strcmp(ptoken, "include") &&
1305                     strcmp(ptoken, "line") &&
1306                     strcmp(ptoken, "else") &&
1307                     strcmp(ptoken, "elif") &&
1308                     strcmp(ptoken, "endif") &&
1309                     strcmp(ptoken, "error") &&
1310                     strcmp(ptoken, "pragma") &&
1311                     strcmp(ptoken, "undef")) {
1312                     if (outFile == NULL) {
1313                         tmpImakefile = Strdup(tmpImakefile);
1314                         mkstemp(tmpImakefile);
1315                         outFile = fopen(tmpImakefile, "w");
1316                         if (outFile == NULL)
1317                             LogFatal("Cannot open %s for write.",
1318                                 tmpImakefile);
1319                     }
1320                     writetmpfile(outFile, punwritten, pbuf-punwritten,
1321                                  tmpImakefile);
1322                     if (ptoken > pbuf + 1)
1323                         writetmpfile(outFile, "XCOMM", 5, tmpImakefile);
1324                     else
1325                         writetmpfile(outFile, "XCOMM ", 6, tmpImakefile);
1326                     punwritten = pbuf + 1;
1327                 }
1328                 *pend = savec;
1329             }
1330             pbuf++;
1331         }
1332         if (outFile) {
1333             writetmpfile(outFile, punwritten, pbuf-punwritten, tmpImakefile);
1334             fclose(outFile);
1335             return tmpImakefile;
1336         }
1337
1338         return(imakefile);
1339 }
1340
1341 static void
1342 CleanCppOutput(FILE *tmpfd, const char *tmpfname)
1343 {
1344         char    *input;
1345         int     blankline = 0;
1346
1347         while ((input = ReadLine(tmpfd, tmpfname))) {
1348                 if (isempty(input)) {
1349                         if (blankline++)
1350                                 continue;
1351                         KludgeResetRule();
1352                 } else {
1353                         blankline = 0;
1354                         KludgeOutputLine(&input);
1355                         writetmpfile(tmpfd, input, strlen(input), tmpfname);
1356                 }
1357                 writetmpfile(tmpfd, "\n", 1, tmpfname);
1358         }
1359         fflush(tmpfd);
1360 #ifdef NFS_STDOUT_BUG
1361         /*
1362          * On some systems, NFS seems to leave a large number of nulls at
1363          * the end of the file.  Ralph Swick says that this kludge makes the
1364          * problem go away.
1365          */
1366         ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
1367 #endif
1368 }
1369
1370 /*
1371  * Determine if a line has nothing in it.  As a side effect, we trim white
1372  * space from the end of the line.  Cpp magic cookies are also thrown away.
1373  * "XCOMM" token is transformed to "#".
1374  */
1375 static boolean
1376 isempty(char *line)
1377 {
1378         char    *pend;
1379
1380         /*
1381          * Check for lines of the form
1382          *      # n "...
1383          * or
1384          *      # line n "...
1385          */
1386         if (*line == '#') {
1387                 pend = line+1;
1388                 if (*pend == ' ')
1389                         pend++;
1390                 if (*pend == 'l' && pend[1] == 'i' && pend[2] == 'n' &&
1391                     pend[3] == 'e' && pend[4] == ' ')
1392                         pend += 5;
1393                 if (isdigit(*pend)) {
1394                         do {
1395                             pend++;
1396                         } while (isdigit(*pend));
1397                         if (*pend == '\n' || *pend == '\0')
1398                                 return(TRUE);
1399                         if (*pend++ == ' ' && *pend == '"')
1400                                 return(TRUE);
1401                 }
1402                 while (*pend)
1403                     pend++;
1404         } else {
1405             for (pend = line; *pend; pend++) {
1406                 if (*pend == 'X' && pend[1] == 'C' && pend[2] == 'O' &&
1407                     pend[3] == 'M' && pend[4] == 'M' &&
1408                     (pend == line || pend[-1] == ' ' || pend[-1] == '\t') &&
1409                     (pend[5] == ' ' || pend[5] == '\t' || pend[5] == '\0'))
1410                 {
1411                     *pend = '#';
1412                     strcpy(pend+1, pend+5);
1413                 }
1414 #ifdef MAGIC_MAKE_VARS
1415                 if (*pend == 'X' && pend[1] == 'V' && pend[2] == 'A' &&
1416                     pend[3] == 'R')
1417                 {
1418                     char varbuf[5];
1419                     int i;
1420
1421                     if (pend[4] == 'd' && pend[5] == 'e' && pend[6] == 'f' &&
1422                         pend[7] >= '0' && pend[7] <= '9')
1423                     {
1424                         i = pend[7] - '0';
1425                         sprintf(varbuf, "%0.4d", xvariable);
1426                         strncpy(pend+4, varbuf, 4);
1427                         xvariables[i] = xvariable;
1428                         xvariable = (xvariable + 1) % 10000;
1429                     }
1430                     else if (pend[4] == 'u' && pend[5] == 's' &&
1431                              pend[6] == 'e' && pend[7] >= '0' &&
1432                              pend[7] <= '9')
1433                     {
1434                         i = pend[7] - '0';
1435                         sprintf(varbuf, "%0.4d", xvariables[i]);
1436                         strncpy(pend+4, varbuf, 4);
1437                     }
1438                 }
1439 #endif
1440             }
1441         }
1442         while (--pend >= line && (*pend == ' ' || *pend == '\t')) ;
1443         pend[1] = '\0';
1444         return (*line == '\0');
1445 }
1446
1447 /*ARGSUSED*/
1448 static char *
1449 ReadLine(FILE *tmpfd, const char *tmpfname)
1450 {
1451         static boolean  initialized = FALSE;
1452         static char     *buf, *pline, *end;
1453         char    *p1, *p2;
1454
1455         if (! initialized) {
1456 #ifdef WIN32
1457                 FILE *fp = tmpfd;
1458 #endif
1459                 int     total_red;
1460                 struct stat     st;
1461
1462                 /*
1463                  * Slurp it all up.
1464                  */
1465                 fseek(tmpfd, 0, 0);
1466                 if (fstat(fileno(tmpfd), &st) < 0)
1467                         LogFatal("cannot stat %s for size", tmpMakefile);
1468                 pline = buf = Emalloc((int)st.st_size+1);
1469                 total_red = fread(buf, 1, st.st_size, tmpfd);
1470                 if (total_red == 0  &&  st.st_size != 0)
1471                         LogFatal("cannot read %s", tmpMakefile);
1472                 end = buf + total_red;
1473                 *end = '\0';
1474                 fseek(tmpfd, 0, 0);
1475 #if defined(SYSV) || defined(WIN32)
1476                 tmpfd = freopen(tmpfname, "w+", tmpfd);
1477 #ifdef WIN32
1478                 if (! tmpfd) /* if failed try again */
1479                         tmpfd = freopen(tmpfname, "w+", fp);
1480 #endif
1481                 if (! tmpfd)
1482                         LogFatal("cannot reopen %s.", tmpfname);
1483 #else   /* !SYSV */
1484                 ftruncate(fileno(tmpfd), (off_t) 0);
1485 #endif  /* !SYSV */
1486                 initialized = TRUE;
1487             fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
1488             fprintf (tmpfd, "# %s\n",
1489                 "$TOG: imake.c /main/104 1998/03/24 12:45:15 kaleb $");
1490         }
1491
1492         for (p1 = pline; p1 < end; p1++) {
1493                 if (*p1 == '@' && *(p1+1) == '@'
1494                     /* ignore ClearCase version-extended pathnames */
1495                     && !(p1 != pline && !isspace(*(p1-1)) && *(p1+2) == '/'))
1496                 { /* soft EOL */
1497                         *p1++ = '\0';
1498                         p1++; /* skip over second @ */
1499                         break;
1500                 }
1501                 else if (*p1 == '\n') { /* real EOL */
1502 #ifdef WIN32
1503                         if (p1 > pline && p1[-1] == '\r')
1504                                 p1[-1] = '\0';
1505 #endif
1506                         *p1++ = '\0';
1507                         break;
1508                 }
1509         }
1510
1511         /*
1512          * return NULL at the end of the file.
1513          */
1514         p2 = (pline == p1 ? NULL : pline);
1515         pline = p1;
1516         return(p2);
1517 }
1518
1519 static void
1520 writetmpfile(FILE *fd, const char *buf, int cnt, const char *fname)
1521 {
1522         if (fwrite(buf, sizeof(char), cnt, fd) == -1)
1523                 LogFatal("Cannot write to %s.", fname);
1524 }
1525
1526 static char *
1527 Emalloc(int size)
1528 {
1529         char    *p;
1530
1531         if ((p = malloc(size)) == NULL)
1532                 LogFatal("Cannot allocate %d bytes.", size);
1533         return(p);
1534 }
1535
1536 #ifdef FIXUP_CPP_WHITESPACE
1537 static void
1538 KludgeOutputLine(char **pline)
1539 {
1540         char    *p = *pline;
1541         char    quotechar = '\0';
1542
1543         switch (*p) {
1544             case '#':   /*Comment - ignore*/
1545                 break;
1546             case '\t':  /*Already tabbed - ignore it*/
1547                 break;
1548             case ' ':   /*May need a tab*/
1549             default:
1550 # ifdef INLINE_SYNTAX
1551                 if (*p == '<' && p[1] == '<') { /* inline file close */
1552                     InInline--;
1553                     InRule = TRUE;
1554                     break;
1555                 }
1556 # endif
1557                 /*
1558                  * The following cases should not be treated as beginning of 
1559                  * rules:
1560                  * variable := name     (GNU make)
1561                  * variable = .*:.*     (':' should be allowed as value)
1562                  *      sed 's:/a:/b:'  (: used in quoted values)
1563                  */
1564                 for (; *p; p++) {
1565                     if (quotechar) {
1566                         if (quotechar == '\\' ||
1567                             (*p == quotechar &&
1568 # ifdef WIN32
1569                              quotechar != ')' &&
1570 # endif
1571                              p[-1] != '\\'))
1572                             quotechar = '\0';
1573                         continue;
1574                     }
1575                     switch (*p) {
1576                     case '\\':
1577                     case '"':
1578                     case '\'':
1579                         quotechar = *p;
1580                         break;
1581                     case '(':
1582                         quotechar = ')';
1583                         break;
1584                     case '{':
1585                         quotechar = '}';
1586                         break;
1587                     case '[':
1588                         quotechar = ']';
1589                         break;
1590                     case '=':
1591 # ifdef REMOVE_CPP_LEADSPACE
1592                         if (!InRule && **pline == ' ') {
1593                             while (**pline == ' ')
1594                                 (*pline)++;
1595                         }
1596 # endif
1597                         goto breakfor;
1598 # ifdef INLINE_SYNTAX
1599                     case '<':
1600                         if (p[1] == '<') /* inline file start */
1601                             InInline++;
1602                         break;
1603 # endif
1604                     case ':':
1605                         if (p[1] == '=')
1606                             goto breakfor;
1607                         while (**pline == ' ')
1608                             (*pline)++;
1609                         InRule = TRUE;
1610                         return;
1611                     }
1612                 }
1613 breakfor:
1614                 if (InRule && **pline == ' ')
1615                     **pline = '\t';
1616                 break;
1617         }
1618 }
1619
1620 static void
1621 KludgeResetRule(void)
1622 {
1623         InRule = FALSE;
1624 }
1625 #endif /* FIXUP_CPP_WHITESPACE */
1626
1627 static char *
1628 Strdup(const char *cp)
1629 {
1630         char *new = Emalloc(strlen(cp) + 1);
1631
1632         strcpy(new, cp);
1633         return new;
1634 }