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