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