160552Selan /* $XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $ */
260552Selan
360552Selan /*****************************************************************************\
460552Selan * *
560552Selan * Porting Note *
660552Selan * *
760552Selan * Add the value of BOOTSTRAPCFLAGS to the cpp_argv table so that it will be *
860552Selan * passed to the template file. *
960552Selan * *
1060552Selan \*****************************************************************************/
1160552Selan
1260552Selan /*
1360552Selan *
1460552Selan * Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
1560552Selan *
1660552Selan * Permission to use, copy, modify, and distribute this
1760552Selan * software and its documentation for any purpose and without
1860552Selan * fee is hereby granted, provided that the above copyright
1960552Selan * notice appear in all copies and that both that copyright
2060552Selan * notice and this permission notice appear in supporting
2160552Selan * documentation, and that the name of M.I.T. not be used in
2260552Selan * advertising or publicity pertaining to distribution of the
2360552Selan * software without specific, written prior permission.
2460552Selan * M.I.T. makes no representations about the suitability of
2560552Selan * this software for any purpose. It is provided "as is"
2660552Selan * without express or implied warranty.
2760552Selan *
2860552Selan * Original Author:
2960552Selan * Todd Brunhoff
3060552Selan * Tektronix, inc.
3160552Selan * While a guest engineer at Project Athena, MIT
3260552Selan *
3360552Selan * imake: the include-make program.
3460552Selan *
3560552Selan * Usage: imake [-Idir] [-Ddefine] [-T] [-f imakefile ] [-s] [-e] [-v] [make flags]
3660552Selan *
3760552Selan * Imake takes a template makefile (Imake.tmpl) and runs cpp on it
3860552Selan * producing a temporary makefile in /tmp. It then runs make on
3960552Selan * this pre-processed makefile.
4060552Selan * Options:
4160552Selan * -D define. Same as cpp -D argument.
4260552Selan * -I Include directory. Same as cpp -I argument.
4360552Selan * -T template. Designate a template other
4460552Selan * than Imake.tmpl
4560552Selan * -s[F] show. Show the produced makefile on the standard
4660552Selan * output. Make is not run is this case. If a file
4760552Selan * argument is provided, the output is placed there.
4860552Selan * -e[F] execute instead of show; optionally name Makefile F
4960552Selan * -v verbose. Show the make command line executed.
5060552Selan *
5160552Selan * Environment variables:
5260552Selan *
5360552Selan * IMAKEINCLUDE Include directory to use in addition to "."
5460552Selan * IMAKECPP Cpp to use instead of /lib/cpp
5560552Selan * IMAKEMAKE make program to use other than what is
5660552Selan * found by searching the $PATH variable.
5760552Selan * Other features:
5860552Selan * imake reads the entire cpp output into memory and then scans it
5960552Selan * for occurences of "@@". If it encounters them, it replaces it with
6060552Selan * a newline. It also trims any trailing white space on output lines
6160552Selan * (because make gets upset at them). This helps when cpp expands
6260552Selan * multi-line macros but you want them to appear on multiple lines.
6360552Selan *
6460552Selan * The macros MAKEFILE and MAKE are provided as macros
6560552Selan * to make. MAKEFILE is set to imake's makefile (not the constructed,
6660552Selan * preprocessed one) and MAKE is set to argv[0], i.e. the name of
6760552Selan * the imake program.
6860552Selan *
6960552Selan * Theory of operation:
7060552Selan * 1. Determine the name of the imakefile from the command line (-f)
7160552Selan * or from the content of the current directory (Imakefile or imakefile).
7260552Selan * Call this <imakefile>. This gets added to the arguments for
7360552Selan * make as MAKEFILE=<imakefile>.
7460552Selan * 2. Determine the name of the template from the command line (-T)
7560552Selan * or the default, Imake.tmpl. Call this <template>
7660552Selan * 3. Start up cpp an provide it with three lines of input:
7760552Selan * #define IMAKE_TEMPLATE " <template> "
7860552Selan * #define INCLUDE_IMAKEFILE < <imakefile> >
7960552Selan * #include IMAKE_TEMPLATE
8060552Selan * Note that the define for INCLUDE_IMAKEFILE is intended for
8160552Selan * use in the template file. This implies that the imake is
8260552Selan * useless unless the template file contains at least the line
8360552Selan * #include INCLUDE_IMAKEFILE
8460552Selan * 4. Gather the output from cpp, and clean it up, expanding @@ to
8560552Selan * newlines, stripping trailing white space, cpp control lines,
8660552Selan * and extra blank lines. This cleaned output is placed in a
8760552Selan * temporary file. Call this <makefile>.
8860552Selan * 5. Start up make specifying <makefile> as its input.
8960552Selan *
9060552Selan * The design of the template makefile should therefore be:
9160552Selan * <set global macros like CFLAGS, etc.>
9260552Selan * <include machine dependent additions>
9360552Selan * #include INCLUDE_IMAKEFILE
9460552Selan * <add any global targets like 'clean' and long dependencies>
9560552Selan */
9660552Selan #include <stdio.h>
9760552Selan #if (defined(SVR4) || defined(_IBMR2) || defined(SYSV386)) && __STDC__
9860552Selan FILE * fdopen();
9960552Selan #endif
10060552Selan #include <ctype.h>
10160552Selan #include "Xosdefs.h"
10260552Selan #ifndef X_NOT_POSIX
10360552Selan #define _POSIX_SOURCE
10460552Selan #endif
10560552Selan #include <sys/types.h>
10660552Selan #include <fcntl.h>
10760552Selan #ifdef X_NOT_POSIX
10860552Selan #include <sys/file.h>
10960552Selan #else
11060552Selan #ifdef hp9000
11160552Selan #undef _POSIX_SOURCE
11260552Selan #endif
11360552Selan #include <unistd.h>
11460552Selan #ifdef hp9000
11560552Selan #define _POSIX_SOURCE
11660552Selan #endif
11760552Selan #endif
11860552Selan #if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
11960552Selan #include <signal.h>
12060552Selan #else
12160552Selan #define _POSIX_SOURCE
12260552Selan #include <signal.h>
12360552Selan #undef _POSIX_SOURCE
12460552Selan #endif
12560552Selan #include <sys/stat.h>
12660552Selan #ifndef X_NOT_POSIX
12760552Selan #ifdef _POSIX_SOURCE
12860552Selan #include <sys/wait.h>
12960552Selan #else
13060552Selan #define _POSIX_SOURCE
13160552Selan #include <sys/wait.h>
13260552Selan #undef _POSIX_SOURCE
13360552Selan #endif
13460552Selan #define waitCode(w) WEXITSTATUS(w)
13560552Selan #define waitSig(w) WTERMSIG(w)
13660552Selan typedef int waitType;
13760552Selan #else /* X_NOT_POSIX */
13860552Selan #ifdef SYSV
13960552Selan #define waitCode(w) (((w) >> 8) & 0x7f)
14060552Selan #define waitSig(w) ((w) & 0xff)
14160552Selan typedef int waitType;
14260552Selan #else /* SYSV */
14360552Selan #include <sys/wait.h>
14460552Selan #define waitCode(w) ((w).w_T.w_Retcode)
14560552Selan #define waitSig(w) ((w).w_T.w_Termsig)
14660552Selan typedef union wait waitType;
14760552Selan #endif
14860552Selan #ifndef WIFSIGNALED
14960552Selan #define WIFSIGNALED(w) waitSig(w)
15060552Selan #endif
15160552Selan #ifndef WIFEXITED
15260552Selan #define WIFEXITED(w) waitCode(w)
15360552Selan #endif
15460552Selan #endif /* X_NOT_POSIX */
15560552Selan #ifndef X_NOT_STDC_ENV
15660552Selan #include <stdlib.h>
15760552Selan #else
15860552Selan char *malloc(), *realloc();
15960552Selan void exit();
16060552Selan #endif
16160552Selan #if defined(macII) && !defined(__STDC__) /* stdlib.h fails to define these */
16260552Selan char *malloc(), *realloc();
16360552Selan #endif /* macII */
16460552Selan #ifdef X_NOT_STDC_ENV
16560552Selan extern char *getenv();
16660552Selan #endif
16760552Selan #include <errno.h>
16860552Selan extern int errno;
16960552Selan #include "imakemdep.h"
17060552Selan
17160552Selan
17260552Selan #define TRUE 1
17360552Selan #define FALSE 0
17460552Selan
17560552Selan #ifdef FIXUP_CPP_WHITESPACE
17660552Selan int InRule = FALSE;
17760552Selan #endif
17860552Selan
17960552Selan /*
18060552Selan * Some versions of cpp reduce all tabs in macro expansion to a single
18160552Selan * space. In addition, the escaped newline may be replaced with a
18260552Selan * space instead of being deleted. Blech.
18360552Selan */
18460552Selan #ifndef FIXUP_CPP_WHITESPACE
18560552Selan #define KludgeOutputLine(arg)
18660552Selan #define KludgeResetRule()
18760552Selan #endif
18860552Selan
18960552Selan typedef unsigned char boolean;
19060552Selan
19160552Selan #ifndef DEFAULT_CPP
19260552Selan #ifdef USE_CC_E
19360552Selan #define DEFAULT_CPP "/bin/cc"
19460552Selan #else
19560552Selan #ifdef CPP_PROGRAM
19660552Selan #define DEFAULT_CPP CPP_PROGRAM
19760552Selan #else
19860552Selan #define DEFAULT_CPP "/lib/cpp"
19960552Selan #endif
20060552Selan #endif
20160552Selan #endif
20260552Selan
20360552Selan char *cpp = DEFAULT_CPP;
20460552Selan
20560552Selan char *tmpMakefile = "/tmp/Imf.XXXXXX";
20660552Selan char *tmpImakefile = "/tmp/IIf.XXXXXX";
20760552Selan char *make_argv[ ARGUMENTS ] = { "make" };
20860552Selan
20960552Selan int make_argindex;
21060552Selan int cpp_argindex;
21160552Selan char *make = NULL;
21260552Selan char *Imakefile = NULL;
21360552Selan char *Makefile = "Makefile";
21460552Selan char *Template = "Imake.tmpl";
21560552Selan char *program;
21660552Selan char *FindImakefile();
21760552Selan char *ReadLine();
21860552Selan char *CleanCppInput();
21960552Selan char *Strdup();
22060552Selan #if defined(__STDC__) || defined(__GNUC__)
22160552Selan char *Emalloc(int);
22260552Selan #else
22360552Selan char *Emalloc();
22460552Selan #endif
22560552Selan
22660552Selan boolean verbose = FALSE;
22760552Selan boolean show = TRUE;
22860552Selan
main(argc,argv)22960552Selan main(argc, argv)
23060552Selan int argc;
23160552Selan char **argv;
23260552Selan {
23360552Selan FILE *tmpfd;
23460552Selan char makeMacro[ BUFSIZ ];
23560552Selan char makefileMacro[ BUFSIZ ];
23660552Selan
23760552Selan program = argv[0];
23860552Selan init();
23960552Selan SetOpts(argc, argv);
24060552Selan #ifdef USE_CC_E
24160552Selan AddCppArg("-");
24260552Selan #endif
24360552Selan
24460552Selan Imakefile = FindImakefile(Imakefile);
24560552Selan if (Makefile)
24660552Selan tmpMakefile = Makefile;
24760552Selan else {
24860552Selan tmpMakefile = Strdup(tmpMakefile);
24960552Selan (void) mktemp(tmpMakefile);
25060552Selan }
25160552Selan AddMakeArg("-f");
25260552Selan AddMakeArg( tmpMakefile );
25360552Selan sprintf(makeMacro, "MAKE=%s", program);
25460552Selan AddMakeArg( makeMacro );
25560552Selan sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
25660552Selan AddMakeArg( makefileMacro );
25760552Selan
25860552Selan if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
25960552Selan LogFatal("Cannot create temporary file %s.", tmpMakefile);
26060552Selan
26160552Selan cppit(Imakefile, Template, tmpfd, tmpMakefile);
26260552Selan
26360552Selan if (show) {
26460552Selan if (Makefile == NULL)
26560552Selan showit(tmpfd);
26660552Selan } else
26760552Selan makeit();
26860552Selan wrapup();
26960552Selan exit(0);
27060552Selan }
27160552Selan
showit(fd)27260552Selan showit(fd)
27360552Selan FILE *fd;
27460552Selan {
27560552Selan char buf[ BUFSIZ ];
27660552Selan int red;
27760552Selan
27860552Selan fseek(fd, 0, 0);
27960552Selan while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
28060552Selan fwrite(buf, red, 1, stdout);
28160552Selan if (red < 0)
28260552Selan LogFatal("Cannot write stdout.", "");
28360552Selan }
28460552Selan
wrapup()28560552Selan wrapup()
28660552Selan {
28760552Selan if (tmpMakefile != Makefile)
28860552Selan unlink(tmpMakefile);
28960552Selan unlink(tmpImakefile);
29060552Selan }
29160552Selan
29260552Selan #ifdef SIGNALRETURNSINT
29360552Selan int
29460552Selan #else
29560552Selan void
29660552Selan #endif
catch(sig)29760552Selan catch(sig)
29860552Selan int sig;
29960552Selan {
30060552Selan errno = 0;
30160552Selan LogFatalI("Signal %d.", sig);
30260552Selan }
30360552Selan
30460552Selan /*
30560552Selan * Initialize some variables.
30660552Selan */
init()30760552Selan init()
30860552Selan {
30960552Selan char *p;
31060552Selan
31160552Selan make_argindex=0;
31260552Selan while (make_argv[ make_argindex ] != NULL)
31360552Selan make_argindex++;
31460552Selan cpp_argindex = 0;
31560552Selan while (cpp_argv[ cpp_argindex ] != NULL)
31660552Selan cpp_argindex++;
31760552Selan
31860552Selan /*
31960552Selan * See if the standard include directory is different than
32060552Selan * the default. Or if cpp is not the default. Or if the make
32160552Selan * found by the PATH variable is not the default.
32260552Selan */
32360552Selan if (p = getenv("IMAKEINCLUDE")) {
32460552Selan if (*p != '-' || *(p+1) != 'I')
32560552Selan LogFatal("Environment var IMAKEINCLUDE %s\n",
32660552Selan "must begin with -I");
32760552Selan AddCppArg(p);
32860552Selan for (; *p; p++)
32960552Selan if (*p == ' ') {
33060552Selan *p++ = '\0';
33160552Selan AddCppArg(p);
33260552Selan }
33360552Selan }
33460552Selan if (p = getenv("IMAKECPP"))
33560552Selan cpp = p;
33660552Selan if (p = getenv("IMAKEMAKE"))
33760552Selan make = p;
33860552Selan
33960552Selan if (signal(SIGINT, SIG_IGN) != SIG_IGN)
34060552Selan signal(SIGINT, catch);
34160552Selan }
34260552Selan
AddMakeArg(arg)34360552Selan AddMakeArg(arg)
34460552Selan char *arg;
34560552Selan {
34660552Selan errno = 0;
34760552Selan if (make_argindex >= ARGUMENTS-1)
34860552Selan LogFatal("Out of internal storage.", "");
34960552Selan make_argv[ make_argindex++ ] = arg;
35060552Selan make_argv[ make_argindex ] = NULL;
35160552Selan }
35260552Selan
AddCppArg(arg)35360552Selan AddCppArg(arg)
35460552Selan char *arg;
35560552Selan {
35660552Selan errno = 0;
35760552Selan if (cpp_argindex >= ARGUMENTS-1)
35860552Selan LogFatal("Out of internal storage.", "");
35960552Selan cpp_argv[ cpp_argindex++ ] = arg;
36060552Selan cpp_argv[ cpp_argindex ] = NULL;
36160552Selan }
36260552Selan
SetOpts(argc,argv)36360552Selan SetOpts(argc, argv)
36460552Selan int argc;
36560552Selan char **argv;
36660552Selan {
36760552Selan errno = 0;
36860552Selan /*
36960552Selan * Now gather the arguments for make
37060552Selan */
37160552Selan for(argc--, argv++; argc; argc--, argv++) {
37260552Selan /*
37360552Selan * We intercept these flags.
37460552Selan */
37560552Selan if (argv[0][0] == '-') {
37660552Selan if (argv[0][1] == 'D') {
37760552Selan AddCppArg(argv[0]);
37860552Selan } else if (argv[0][1] == 'I') {
37960552Selan AddCppArg(argv[0]);
38060552Selan } else if (argv[0][1] == 'f') {
38160552Selan if (argv[0][2])
38260552Selan Imakefile = argv[0]+2;
38360552Selan else {
38460552Selan argc--, argv++;
38560552Selan if (! argc)
38660552Selan LogFatal("No description arg after -f flag\n", "");
38760552Selan Imakefile = argv[0];
38860552Selan }
38960552Selan } else if (argv[0][1] == 's') {
39060552Selan if (argv[0][2])
39160552Selan Makefile = ((argv[0][2] == '-') && !argv[0][3]) ?
39260552Selan NULL : argv[0]+2;
39360552Selan else {
39460552Selan argc--, argv++;
39560552Selan if (!argc)
39660552Selan LogFatal("No description arg after -s flag\n", "");
39760552Selan Makefile = ((argv[0][0] == '-') && !argv[0][1]) ?
39860552Selan NULL : argv[0];
39960552Selan }
40060552Selan show = TRUE;
40160552Selan } else if (argv[0][1] == 'e') {
40260552Selan Makefile = (argv[0][2] ? argv[0]+2 : NULL);
40360552Selan show = FALSE;
40460552Selan } else if (argv[0][1] == 'T') {
40560552Selan if (argv[0][2])
40660552Selan Template = argv[0]+2;
40760552Selan else {
40860552Selan argc--, argv++;
40960552Selan if (! argc)
41060552Selan LogFatal("No description arg after -T flag\n", "");
41160552Selan Template = argv[0];
41260552Selan }
41360552Selan } else if (argv[0][1] == 'v') {
41460552Selan verbose = TRUE;
41560552Selan } else
41660552Selan AddMakeArg(argv[0]);
41760552Selan } else
41860552Selan AddMakeArg(argv[0]);
41960552Selan }
42060552Selan }
42160552Selan
FindImakefile(Imakefile)42260552Selan char *FindImakefile(Imakefile)
42360552Selan char *Imakefile;
42460552Selan {
42560552Selan int fd;
42660552Selan
42760552Selan if (Imakefile) {
42860552Selan if ((fd = open(Imakefile, O_RDONLY)) < 0)
42960552Selan LogFatal("Cannot open %s.", Imakefile);
43060552Selan } else {
43160552Selan if ((fd = open("Imakefile", O_RDONLY)) < 0)
43260552Selan if ((fd = open("imakefile", O_RDONLY)) < 0)
43360552Selan LogFatal("No description file.", "");
43460552Selan else
43560552Selan Imakefile = "imakefile";
43660552Selan else
43760552Selan Imakefile = "Imakefile";
43860552Selan }
43960552Selan close (fd);
44060552Selan return(Imakefile);
44160552Selan }
44260552Selan
LogFatalI(s,i)44360552Selan LogFatalI(s, i)
44460552Selan char *s;
44560552Selan int i;
44660552Selan {
44760552Selan /*NOSTRICT*/
44860552Selan LogFatal(s, (char *)i);
44960552Selan }
45060552Selan
LogFatal(x0,x1)45160552Selan LogFatal(x0,x1)
45260552Selan char *x0, *x1;
45360552Selan {
45460552Selan extern char *sys_errlist[];
45560552Selan static boolean entered = FALSE;
45660552Selan
45760552Selan if (entered)
45860552Selan return;
45960552Selan entered = TRUE;
46060552Selan
46160552Selan fprintf(stderr, "%s: ", program);
46260552Selan if (errno)
46360552Selan fprintf(stderr, "%s: ", sys_errlist[ errno ]);
46460552Selan fprintf(stderr, x0,x1);
46560552Selan fprintf(stderr, " Stop.\n");
46660552Selan wrapup();
46760552Selan exit(1);
46860552Selan }
46960552Selan
showargs(argv)47060552Selan showargs(argv)
47160552Selan char **argv;
47260552Selan {
47360552Selan for (; *argv; argv++)
47460552Selan fprintf(stderr, "%s ", *argv);
47560552Selan fprintf(stderr, "\n");
47660552Selan }
47760552Selan
cppit(Imakefile,template,outfd,outfname)47860552Selan cppit(Imakefile, template, outfd, outfname)
47960552Selan char *Imakefile;
48060552Selan char *template;
48160552Selan FILE *outfd;
48260552Selan char *outfname;
48360552Selan {
48460552Selan FILE *pipeFile;
48560552Selan int pid, pipefd[2];
48660552Selan waitType status;
48760552Selan char *cleanedImakefile;
48860552Selan
48960552Selan /*
49060552Selan * Get a pipe.
49160552Selan */
49260552Selan if (pipe(pipefd) < 0)
49360552Selan LogFatal("Cannot make a pipe.", "");
49460552Selan
49560552Selan /*
49660552Selan * Fork and exec cpp
49760552Selan */
49860552Selan pid = fork();
49960552Selan if (pid < 0)
50060552Selan LogFatal("Cannot fork.", "");
50160552Selan if (pid) { /* parent */
50260552Selan close(pipefd[0]);
50360552Selan cleanedImakefile = CleanCppInput(Imakefile);
50460552Selan if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
50560552Selan LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
50660552Selan fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
50760552Selan template);
50860552Selan fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t<%s>\n",
50960552Selan cleanedImakefile);
51060552Selan fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
51160552Selan fclose(pipeFile);
51260552Selan while (wait(&status) > 0) {
51360552Selan errno = 0;
51460552Selan if (WIFSIGNALED(status))
51560552Selan LogFatalI("Signal %d.", waitSig(status));
51660552Selan if (WIFEXITED(status) && waitCode(status))
51760552Selan LogFatalI("Exit code %d.", waitCode(status));
51860552Selan }
51960552Selan CleanCppOutput(outfd, outfname);
52060552Selan } else { /* child... dup and exec cpp */
52160552Selan if (verbose)
52260552Selan showargs(cpp_argv);
52360552Selan dup2(pipefd[0], 0);
52460552Selan dup2(fileno(outfd), 1);
52560552Selan close(pipefd[1]);
52660552Selan execv(cpp, cpp_argv);
52760552Selan LogFatal("Cannot exec %s.", cpp);
52860552Selan }
52960552Selan }
53060552Selan
makeit()53160552Selan makeit()
53260552Selan {
53360552Selan int pid;
53460552Selan waitType status;
53560552Selan
53660552Selan /*
53760552Selan * Fork and exec make
53860552Selan */
53960552Selan pid = fork();
54060552Selan if (pid < 0)
54160552Selan LogFatal("Cannot fork.", "");
54260552Selan if (pid) { /* parent... simply wait */
54360552Selan while (wait(&status) > 0) {
54460552Selan errno = 0;
54560552Selan if (WIFSIGNALED(status))
54660552Selan LogFatalI("Signal %d.", waitSig(status));
54760552Selan if (WIFEXITED(status) && waitCode(status))
54860552Selan LogFatalI("Exit code %d.", waitCode(status));
54960552Selan }
55060552Selan } else { /* child... dup and exec cpp */
55160552Selan if (verbose)
55260552Selan showargs(make_argv);
55360552Selan if (make)
55460552Selan execv(make, make_argv);
55560552Selan else
55660552Selan execvp("make", make_argv);
55760552Selan LogFatal("Cannot exec %s.", make);
55860552Selan }
55960552Selan }
56060552Selan
CleanCppInput(Imakefile)56160552Selan char *CleanCppInput(Imakefile)
56260552Selan char *Imakefile;
56360552Selan {
56460552Selan FILE *outFile = NULL;
56560552Selan int infd;
56660552Selan char *buf, /* buffer for file content */
56760552Selan *pbuf, /* walking pointer to buf */
56860552Selan *punwritten, /* pointer to unwritten portion of buf */
56960552Selan *cleanedImakefile = Imakefile, /* return value */
57060552Selan *ptoken, /* pointer to # token */
57160552Selan *pend, /* pointer to end of # token */
57260552Selan savec; /* temporary character holder */
57360552Selan struct stat st;
57460552Selan
57560552Selan /*
57660552Selan * grab the entire file.
57760552Selan */
57860552Selan if ((infd = open(Imakefile, O_RDONLY)) < 0)
57960552Selan LogFatal("Cannot open %s for input.", Imakefile);
58060552Selan fstat(infd, &st);
58160552Selan buf = Emalloc(st.st_size+1);
582*60923Selan if (read(infd, buf, (size_t) st.st_size) != st.st_size)
58360552Selan LogFatal("Cannot read all of %s:", Imakefile);
58460552Selan close(infd);
58560552Selan buf[ st.st_size ] = '\0';
58660552Selan
58760552Selan punwritten = pbuf = buf;
58860552Selan while (*pbuf) {
58960552Selan /* pad make comments for cpp */
59060552Selan if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
59160552Selan
59260552Selan ptoken = pbuf+1;
59360552Selan while (*ptoken == ' ' || *ptoken == '\t')
59460552Selan ptoken++;
59560552Selan pend = ptoken;
59660552Selan while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
59760552Selan pend++;
59860552Selan savec = *pend;
59960552Selan *pend = '\0';
60060552Selan if (strcmp(ptoken, "include")
60160552Selan && strcmp(ptoken, "define")
60260552Selan && strcmp(ptoken, "undef")
60360552Selan && strcmp(ptoken, "ifdef")
60460552Selan && strcmp(ptoken, "ifndef")
60560552Selan && strcmp(ptoken, "else")
60660552Selan && strcmp(ptoken, "endif")
60760552Selan && strcmp(ptoken, "if")) {
60860552Selan if (outFile == NULL) {
60960552Selan tmpImakefile = Strdup(tmpImakefile);
61060552Selan (void) mktemp(tmpImakefile);
61160552Selan cleanedImakefile = tmpImakefile;
61260552Selan outFile = fopen(tmpImakefile, "w");
61360552Selan if (outFile == NULL)
61460552Selan LogFatal("Cannot open %s for write.\n",
61560552Selan tmpImakefile);
61660552Selan }
61760552Selan fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
61860552Selan fputs("/**/", outFile);
61960552Selan punwritten = pbuf;
62060552Selan }
62160552Selan *pend = savec;
62260552Selan }
62360552Selan pbuf++;
62460552Selan }
62560552Selan if (outFile) {
62660552Selan fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
62760552Selan fclose(outFile); /* also closes the pipe */
62860552Selan }
62960552Selan
63060552Selan return(cleanedImakefile);
63160552Selan }
63260552Selan
CleanCppOutput(tmpfd,tmpfname)63360552Selan CleanCppOutput(tmpfd, tmpfname)
63460552Selan FILE *tmpfd;
63560552Selan char *tmpfname;
63660552Selan {
63760552Selan char *input;
63860552Selan int blankline = 0;
63960552Selan
64060552Selan while(input = ReadLine(tmpfd, tmpfname)) {
64160552Selan if (isempty(input)) {
64260552Selan if (blankline++)
64360552Selan continue;
64460552Selan KludgeResetRule();
64560552Selan } else {
64660552Selan blankline = 0;
64760552Selan KludgeOutputLine(&input);
64860552Selan fputs(input, tmpfd);
64960552Selan }
65060552Selan putc('\n', tmpfd);
65160552Selan }
65260552Selan fflush(tmpfd);
65360552Selan #ifdef NFS_STDOUT_BUG
65460552Selan /*
65560552Selan * On some systems, NFS seems to leave a large number of nulls at
65660552Selan * the end of the file. Ralph Swick says that this kludge makes the
65760552Selan * problem go away.
65860552Selan */
65960552Selan ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
66060552Selan #endif
66160552Selan }
66260552Selan
66360552Selan /*
66460552Selan * Determine of a line has nothing in it. As a side effect, we trim white
66560552Selan * space from the end of the line. Cpp magic cookies are also thrown away.
66660552Selan */
isempty(line)66760552Selan isempty(line)
66860552Selan char *line;
66960552Selan {
67060552Selan char *pend;
67160552Selan
67260552Selan /*
67360552Selan * Check for lines of the form
67460552Selan * # n "...
67560552Selan * or
67660552Selan * # line n "...
67760552Selan */
67860552Selan if (*line == '#') {
67960552Selan pend = line+1;
68060552Selan if (*pend == ' ')
68160552Selan pend++;
68260552Selan if (strncmp(pend, "line ", 5) == 0)
68360552Selan pend += 5;
68460552Selan if (isdigit(*pend)) {
68560552Selan while (isdigit(*pend))
68660552Selan pend++;
68760552Selan if (*pend++ == ' ' && *pend == '"')
68860552Selan return(TRUE);
68960552Selan }
69060552Selan }
69160552Selan
69260552Selan /*
69360552Selan * Find the end of the line and then walk back.
69460552Selan */
69560552Selan for (pend=line; *pend; pend++) ;
69660552Selan
69760552Selan pend--;
69860552Selan while (pend >= line && (*pend == ' ' || *pend == '\t'))
69960552Selan pend--;
70060552Selan *++pend = '\0';
70160552Selan return (*line == '\0');
70260552Selan }
70360552Selan
70460552Selan /*ARGSUSED*/
ReadLine(tmpfd,tmpfname)70560552Selan char *ReadLine(tmpfd, tmpfname)
70660552Selan FILE *tmpfd;
70760552Selan char *tmpfname;
70860552Selan {
70960552Selan static boolean initialized = FALSE;
71060552Selan static char *buf, *pline, *end;
71160552Selan char *p1, *p2;
71260552Selan
71360552Selan if (! initialized) {
71460552Selan int total_red;
71560552Selan struct stat st;
71660552Selan
71760552Selan /*
71860552Selan * Slurp it all up.
71960552Selan */
72060552Selan fseek(tmpfd, 0, 0);
72160552Selan fstat(fileno(tmpfd), &st);
72260552Selan pline = buf = Emalloc(st.st_size+1);
72360552Selan total_red = read(fileno(tmpfd), buf, st.st_size);
72460552Selan if (total_red != st.st_size)
72560552Selan LogFatal("cannot read %s\n", tmpMakefile);
72660552Selan end = buf + st.st_size;
72760552Selan *end = '\0';
72860552Selan lseek(fileno(tmpfd), 0, 0);
72960552Selan #ifdef SYSV
73060552Selan freopen(tmpfname, "w+", tmpfd);
73160552Selan #else /* !SYSV */
73260552Selan ftruncate(fileno(tmpfd), 0);
73360552Selan #endif /* !SYSV */
73460552Selan initialized = TRUE;
73560552Selan fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
73660552Selan fprintf (tmpfd, "# %s\n",
73760552Selan "$XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $");
73860552Selan
73960552Selan #ifdef FIXUP_CPP_WHITESPACE
74060552Selan {
74160552Selan static char *cpp_warning[] = {
74260552Selan "#",
74360552Selan "# The cpp used on this machine replaces all newlines and multiple tabs and",
74460552Selan "# spaces in a macro expansion with a single space. Imake tries to compensate",
74560552Selan "# for this, but is not always successful.",
74660552Selan "#",
74760552Selan NULL };
74860552Selan char **cpp;
74960552Selan
75060552Selan for (cpp = cpp_warning; *cpp; cpp++) {
75160552Selan fprintf (tmpfd, "%s\n", *cpp);
75260552Selan }
75360552Selan }
75460552Selan #endif /* FIXUP_CPP_WHITESPACE */
75560552Selan }
75660552Selan
75760552Selan for (p1 = pline; p1 < end; p1++) {
75860552Selan if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
75960552Selan *p1++ = '\0';
76060552Selan p1++; /* skip over second @ */
76160552Selan break;
76260552Selan }
76360552Selan else if (*p1 == '\n') { /* real EOL */
76460552Selan *p1++ = '\0';
76560552Selan break;
76660552Selan }
76760552Selan }
76860552Selan
76960552Selan /*
77060552Selan * return NULL at the end of the file.
77160552Selan */
77260552Selan p2 = (pline == p1 ? NULL : pline);
77360552Selan pline = p1;
77460552Selan return(p2);
77560552Selan }
77660552Selan
writetmpfile(fd,buf,cnt)77760552Selan writetmpfile(fd, buf, cnt)
77860552Selan FILE *fd;
77960552Selan int cnt;
78060552Selan char *buf;
78160552Selan {
78260552Selan errno = 0;
78360552Selan if (fwrite(buf, cnt, 1, fd) != 1)
78460552Selan LogFatal("Cannot write to %s.", tmpMakefile);
78560552Selan }
78660552Selan
Emalloc(size)78760552Selan char *Emalloc(size)
78860552Selan int size;
78960552Selan {
79060552Selan char *p;
79160552Selan
79260552Selan if ((p = malloc(size)) == NULL)
79360552Selan LogFatalI("Cannot allocate %d bytes\n", size);
79460552Selan return(p);
79560552Selan }
79660552Selan
79760552Selan #ifdef FIXUP_CPP_WHITESPACE
KludgeOutputLine(pline)79860552Selan KludgeOutputLine(pline)
79960552Selan char **pline;
80060552Selan {
80160552Selan char *p = *pline;
80260552Selan
80360552Selan switch (*p) {
80460552Selan case '#': /*Comment - ignore*/
80560552Selan break;
80660552Selan case '\t': /*Already tabbed - ignore it*/
80760552Selan break;
80860552Selan case ' ': /*May need a tab*/
80960552Selan default:
81060552Selan for (; *p; p++) if (p[0] == ':' &&
81160552Selan p > *pline && p[-1] != '\\') {
81260552Selan if (**pline == ' ')
81360552Selan (*pline)++;
81460552Selan InRule = TRUE;
81560552Selan break;
81660552Selan }
81760552Selan if (InRule && **pline == ' ')
81860552Selan **pline = '\t';
81960552Selan break;
82060552Selan }
82160552Selan }
82260552Selan
KludgeResetRule()82360552Selan KludgeResetRule()
82460552Selan {
82560552Selan InRule = FALSE;
82660552Selan }
82760552Selan #endif /* FIXUP_CPP_WHITESPACE */
82860552Selan
Strdup(cp)82960552Selan char *Strdup(cp)
83060552Selan register char *cp;
83160552Selan {
83260552Selan register char *new = Emalloc(strlen(cp) + 1);
83360552Selan
83460552Selan strcpy(new, cp);
83560552Selan return new;
83660552Selan }
837