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