xref: /csrg-svn/local/X11R5/mit/config/imake.c (revision 60552)
1*60552Selan /* $XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $ */
2*60552Selan 
3*60552Selan /*****************************************************************************\
4*60552Selan  *                                                                           *
5*60552Selan  *                                Porting Note                               *
6*60552Selan  *                                                                           *
7*60552Selan  * Add the value of BOOTSTRAPCFLAGS to the cpp_argv table so that it will be *
8*60552Selan  * passed to the template file.                                              *
9*60552Selan  *                                                                           *
10*60552Selan \*****************************************************************************/
11*60552Selan 
12*60552Selan /*
13*60552Selan  *
14*60552Selan  * Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
15*60552Selan  *
16*60552Selan  * Permission to use, copy, modify, and distribute this
17*60552Selan  * software and its documentation for any purpose and without
18*60552Selan  * fee is hereby granted, provided that the above copyright
19*60552Selan  * notice appear in all copies and that both that copyright
20*60552Selan  * notice and this permission notice appear in supporting
21*60552Selan  * documentation, and that the name of M.I.T. not be used in
22*60552Selan  * advertising or publicity pertaining to distribution of the
23*60552Selan  * software without specific, written prior permission.
24*60552Selan  * M.I.T. makes no representations about the suitability of
25*60552Selan  * this software for any purpose.  It is provided "as is"
26*60552Selan  * without express or implied warranty.
27*60552Selan  *
28*60552Selan  * Original Author:
29*60552Selan  *	Todd Brunhoff
30*60552Selan  *	Tektronix, inc.
31*60552Selan  *	While a guest engineer at Project Athena, MIT
32*60552Selan  *
33*60552Selan  * imake: the include-make program.
34*60552Selan  *
35*60552Selan  * Usage: imake [-Idir] [-Ddefine] [-T] [-f imakefile ] [-s] [-e] [-v] [make flags]
36*60552Selan  *
37*60552Selan  * Imake takes a template makefile (Imake.tmpl) and runs cpp on it
38*60552Selan  * producing a temporary makefile in /tmp.  It then runs make on
39*60552Selan  * this pre-processed makefile.
40*60552Selan  * Options:
41*60552Selan  *		-D	define.  Same as cpp -D argument.
42*60552Selan  *		-I	Include directory.  Same as cpp -I argument.
43*60552Selan  *		-T	template.  Designate a template other
44*60552Selan  * 			than Imake.tmpl
45*60552Selan  *		-s[F]	show.  Show the produced makefile on the standard
46*60552Selan  *			output.  Make is not run is this case.  If a file
47*60552Selan  *			argument is provided, the output is placed there.
48*60552Selan  *              -e[F]   execute instead of show; optionally name Makefile F
49*60552Selan  *		-v	verbose.  Show the make command line executed.
50*60552Selan  *
51*60552Selan  * Environment variables:
52*60552Selan  *
53*60552Selan  *		IMAKEINCLUDE	Include directory to use in addition to "."
54*60552Selan  *		IMAKECPP	Cpp to use instead of /lib/cpp
55*60552Selan  *		IMAKEMAKE	make program to use other than what is
56*60552Selan  *				found by searching the $PATH variable.
57*60552Selan  * Other features:
58*60552Selan  *	imake reads the entire cpp output into memory and then scans it
59*60552Selan  *	for occurences of "@@".  If it encounters them, it replaces it with
60*60552Selan  *	a newline.  It also trims any trailing white space on output lines
61*60552Selan  *	(because make gets upset at them).  This helps when cpp expands
62*60552Selan  *	multi-line macros but you want them to appear on multiple lines.
63*60552Selan  *
64*60552Selan  *	The macros MAKEFILE and MAKE are provided as macros
65*60552Selan  *	to make.  MAKEFILE is set to imake's makefile (not the constructed,
66*60552Selan  *	preprocessed one) and MAKE is set to argv[0], i.e. the name of
67*60552Selan  *	the imake program.
68*60552Selan  *
69*60552Selan  * Theory of operation:
70*60552Selan  *   1. Determine the name of the imakefile from the command line (-f)
71*60552Selan  *	or from the content of the current directory (Imakefile or imakefile).
72*60552Selan  *	Call this <imakefile>.  This gets added to the arguments for
73*60552Selan  *	make as MAKEFILE=<imakefile>.
74*60552Selan  *   2. Determine the name of the template from the command line (-T)
75*60552Selan  *	or the default, Imake.tmpl.  Call this <template>
76*60552Selan  *   3. Start up cpp an provide it with three lines of input:
77*60552Selan  *		#define IMAKE_TEMPLATE		" <template> "
78*60552Selan  *		#define INCLUDE_IMAKEFILE	< <imakefile> >
79*60552Selan  *		#include IMAKE_TEMPLATE
80*60552Selan  *	Note that the define for INCLUDE_IMAKEFILE is intended for
81*60552Selan  *	use in the template file.  This implies that the imake is
82*60552Selan  *	useless unless the template file contains at least the line
83*60552Selan  *		#include INCLUDE_IMAKEFILE
84*60552Selan  *   4. Gather the output from cpp, and clean it up, expanding @@ to
85*60552Selan  *	newlines, stripping trailing white space, cpp control lines,
86*60552Selan  *	and extra blank lines.  This cleaned output is placed in a
87*60552Selan  *	temporary file.  Call this <makefile>.
88*60552Selan  *   5. Start up make specifying <makefile> as its input.
89*60552Selan  *
90*60552Selan  * The design of the template makefile should therefore be:
91*60552Selan  *	<set global macros like CFLAGS, etc.>
92*60552Selan  *	<include machine dependent additions>
93*60552Selan  *	#include INCLUDE_IMAKEFILE
94*60552Selan  *	<add any global targets like 'clean' and long dependencies>
95*60552Selan  */
96*60552Selan #include <stdio.h>
97*60552Selan #if (defined(SVR4) || defined(_IBMR2) || defined(SYSV386)) && __STDC__
98*60552Selan FILE * fdopen();
99*60552Selan #endif
100*60552Selan #include <ctype.h>
101*60552Selan #include "Xosdefs.h"
102*60552Selan #ifndef X_NOT_POSIX
103*60552Selan #define _POSIX_SOURCE
104*60552Selan #endif
105*60552Selan #include <sys/types.h>
106*60552Selan #include <fcntl.h>
107*60552Selan #ifdef X_NOT_POSIX
108*60552Selan #include <sys/file.h>
109*60552Selan #else
110*60552Selan #ifdef hp9000
111*60552Selan #undef _POSIX_SOURCE
112*60552Selan #endif
113*60552Selan #include <unistd.h>
114*60552Selan #ifdef hp9000
115*60552Selan #define _POSIX_SOURCE
116*60552Selan #endif
117*60552Selan #endif
118*60552Selan #if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
119*60552Selan #include <signal.h>
120*60552Selan #else
121*60552Selan #define _POSIX_SOURCE
122*60552Selan #include <signal.h>
123*60552Selan #undef _POSIX_SOURCE
124*60552Selan #endif
125*60552Selan #include <sys/stat.h>
126*60552Selan #ifndef X_NOT_POSIX
127*60552Selan #ifdef _POSIX_SOURCE
128*60552Selan #include <sys/wait.h>
129*60552Selan #else
130*60552Selan #define _POSIX_SOURCE
131*60552Selan #include <sys/wait.h>
132*60552Selan #undef _POSIX_SOURCE
133*60552Selan #endif
134*60552Selan #define waitCode(w)	WEXITSTATUS(w)
135*60552Selan #define waitSig(w)	WTERMSIG(w)
136*60552Selan typedef int		waitType;
137*60552Selan #else /* X_NOT_POSIX */
138*60552Selan #ifdef SYSV
139*60552Selan #define waitCode(w)	(((w) >> 8) & 0x7f)
140*60552Selan #define waitSig(w)	((w) & 0xff)
141*60552Selan typedef int		waitType;
142*60552Selan #else /* SYSV */
143*60552Selan #include <sys/wait.h>
144*60552Selan #define waitCode(w)	((w).w_T.w_Retcode)
145*60552Selan #define waitSig(w)	((w).w_T.w_Termsig)
146*60552Selan typedef union wait	waitType;
147*60552Selan #endif
148*60552Selan #ifndef WIFSIGNALED
149*60552Selan #define WIFSIGNALED(w) waitSig(w)
150*60552Selan #endif
151*60552Selan #ifndef WIFEXITED
152*60552Selan #define WIFEXITED(w) waitCode(w)
153*60552Selan #endif
154*60552Selan #endif /* X_NOT_POSIX */
155*60552Selan #ifndef X_NOT_STDC_ENV
156*60552Selan #include <stdlib.h>
157*60552Selan #else
158*60552Selan char *malloc(), *realloc();
159*60552Selan void exit();
160*60552Selan #endif
161*60552Selan #if defined(macII) && !defined(__STDC__)  /* stdlib.h fails to define these */
162*60552Selan char *malloc(), *realloc();
163*60552Selan #endif /* macII */
164*60552Selan #ifdef X_NOT_STDC_ENV
165*60552Selan extern char	*getenv();
166*60552Selan #endif
167*60552Selan #include <errno.h>
168*60552Selan extern int	errno;
169*60552Selan #include "imakemdep.h"
170*60552Selan 
171*60552Selan 
172*60552Selan #define	TRUE		1
173*60552Selan #define	FALSE		0
174*60552Selan 
175*60552Selan #ifdef FIXUP_CPP_WHITESPACE
176*60552Selan int	InRule = FALSE;
177*60552Selan #endif
178*60552Selan 
179*60552Selan /*
180*60552Selan  * Some versions of cpp reduce all tabs in macro expansion to a single
181*60552Selan  * space.  In addition, the escaped newline may be replaced with a
182*60552Selan  * space instead of being deleted.  Blech.
183*60552Selan  */
184*60552Selan #ifndef FIXUP_CPP_WHITESPACE
185*60552Selan #define KludgeOutputLine(arg)
186*60552Selan #define KludgeResetRule()
187*60552Selan #endif
188*60552Selan 
189*60552Selan typedef	unsigned char	boolean;
190*60552Selan 
191*60552Selan #ifndef DEFAULT_CPP
192*60552Selan #ifdef USE_CC_E
193*60552Selan #define DEFAULT_CPP "/bin/cc"
194*60552Selan #else
195*60552Selan #ifdef CPP_PROGRAM
196*60552Selan #define DEFAULT_CPP CPP_PROGRAM
197*60552Selan #else
198*60552Selan #define DEFAULT_CPP "/lib/cpp"
199*60552Selan #endif
200*60552Selan #endif
201*60552Selan #endif
202*60552Selan 
203*60552Selan char *cpp = DEFAULT_CPP;
204*60552Selan 
205*60552Selan char	*tmpMakefile    = "/tmp/Imf.XXXXXX";
206*60552Selan char	*tmpImakefile    = "/tmp/IIf.XXXXXX";
207*60552Selan char	*make_argv[ ARGUMENTS ] = { "make" };
208*60552Selan 
209*60552Selan int	make_argindex;
210*60552Selan int	cpp_argindex;
211*60552Selan char	*make = NULL;
212*60552Selan char	*Imakefile = NULL;
213*60552Selan char	*Makefile = "Makefile";
214*60552Selan char	*Template = "Imake.tmpl";
215*60552Selan char	*program;
216*60552Selan char	*FindImakefile();
217*60552Selan char	*ReadLine();
218*60552Selan char	*CleanCppInput();
219*60552Selan char	*Strdup();
220*60552Selan #if defined(__STDC__) || defined(__GNUC__)
221*60552Selan char	*Emalloc(int);
222*60552Selan #else
223*60552Selan char	*Emalloc();
224*60552Selan #endif
225*60552Selan 
226*60552Selan boolean	verbose = FALSE;
227*60552Selan boolean	show = TRUE;
228*60552Selan 
229*60552Selan main(argc, argv)
230*60552Selan 	int	argc;
231*60552Selan 	char	**argv;
232*60552Selan {
233*60552Selan 	FILE	*tmpfd;
234*60552Selan 	char	makeMacro[ BUFSIZ ];
235*60552Selan 	char	makefileMacro[ BUFSIZ ];
236*60552Selan 
237*60552Selan 	program = argv[0];
238*60552Selan 	init();
239*60552Selan 	SetOpts(argc, argv);
240*60552Selan #ifdef USE_CC_E
241*60552Selan 	AddCppArg("-");
242*60552Selan #endif
243*60552Selan 
244*60552Selan 	Imakefile = FindImakefile(Imakefile);
245*60552Selan 	if (Makefile)
246*60552Selan 		tmpMakefile = Makefile;
247*60552Selan 	else {
248*60552Selan 		tmpMakefile = Strdup(tmpMakefile);
249*60552Selan 		(void) mktemp(tmpMakefile);
250*60552Selan 	}
251*60552Selan 	AddMakeArg("-f");
252*60552Selan 	AddMakeArg( tmpMakefile );
253*60552Selan 	sprintf(makeMacro, "MAKE=%s", program);
254*60552Selan 	AddMakeArg( makeMacro );
255*60552Selan 	sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
256*60552Selan 	AddMakeArg( makefileMacro );
257*60552Selan 
258*60552Selan 	if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
259*60552Selan 		LogFatal("Cannot create temporary file %s.", tmpMakefile);
260*60552Selan 
261*60552Selan 	cppit(Imakefile, Template, tmpfd, tmpMakefile);
262*60552Selan 
263*60552Selan 	if (show) {
264*60552Selan 		if (Makefile == NULL)
265*60552Selan 			showit(tmpfd);
266*60552Selan 	} else
267*60552Selan 		makeit();
268*60552Selan 	wrapup();
269*60552Selan 	exit(0);
270*60552Selan }
271*60552Selan 
272*60552Selan showit(fd)
273*60552Selan 	FILE	*fd;
274*60552Selan {
275*60552Selan 	char	buf[ BUFSIZ ];
276*60552Selan 	int	red;
277*60552Selan 
278*60552Selan 	fseek(fd, 0, 0);
279*60552Selan 	while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
280*60552Selan 		fwrite(buf, red, 1, stdout);
281*60552Selan 	if (red < 0)
282*60552Selan 		LogFatal("Cannot write stdout.", "");
283*60552Selan }
284*60552Selan 
285*60552Selan wrapup()
286*60552Selan {
287*60552Selan 	if (tmpMakefile != Makefile)
288*60552Selan 		unlink(tmpMakefile);
289*60552Selan 	unlink(tmpImakefile);
290*60552Selan }
291*60552Selan 
292*60552Selan #ifdef SIGNALRETURNSINT
293*60552Selan int
294*60552Selan #else
295*60552Selan void
296*60552Selan #endif
297*60552Selan catch(sig)
298*60552Selan 	int	sig;
299*60552Selan {
300*60552Selan 	errno = 0;
301*60552Selan 	LogFatalI("Signal %d.", sig);
302*60552Selan }
303*60552Selan 
304*60552Selan /*
305*60552Selan  * Initialize some variables.
306*60552Selan  */
307*60552Selan init()
308*60552Selan {
309*60552Selan 	char	*p;
310*60552Selan 
311*60552Selan 	make_argindex=0;
312*60552Selan 	while (make_argv[ make_argindex ] != NULL)
313*60552Selan 		make_argindex++;
314*60552Selan 	cpp_argindex = 0;
315*60552Selan 	while (cpp_argv[ cpp_argindex ] != NULL)
316*60552Selan 		cpp_argindex++;
317*60552Selan 
318*60552Selan 	/*
319*60552Selan 	 * See if the standard include directory is different than
320*60552Selan 	 * the default.  Or if cpp is not the default.  Or if the make
321*60552Selan 	 * found by the PATH variable is not the default.
322*60552Selan 	 */
323*60552Selan 	if (p = getenv("IMAKEINCLUDE")) {
324*60552Selan 		if (*p != '-' || *(p+1) != 'I')
325*60552Selan 			LogFatal("Environment var IMAKEINCLUDE %s\n",
326*60552Selan 				"must begin with -I");
327*60552Selan 		AddCppArg(p);
328*60552Selan 		for (; *p; p++)
329*60552Selan 			if (*p == ' ') {
330*60552Selan 				*p++ = '\0';
331*60552Selan 				AddCppArg(p);
332*60552Selan 			}
333*60552Selan 	}
334*60552Selan 	if (p = getenv("IMAKECPP"))
335*60552Selan 		cpp = p;
336*60552Selan 	if (p = getenv("IMAKEMAKE"))
337*60552Selan 		make = p;
338*60552Selan 
339*60552Selan 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
340*60552Selan 		signal(SIGINT, catch);
341*60552Selan }
342*60552Selan 
343*60552Selan AddMakeArg(arg)
344*60552Selan 	char	*arg;
345*60552Selan {
346*60552Selan 	errno = 0;
347*60552Selan 	if (make_argindex >= ARGUMENTS-1)
348*60552Selan 		LogFatal("Out of internal storage.", "");
349*60552Selan 	make_argv[ make_argindex++ ] = arg;
350*60552Selan 	make_argv[ make_argindex ] = NULL;
351*60552Selan }
352*60552Selan 
353*60552Selan AddCppArg(arg)
354*60552Selan 	char	*arg;
355*60552Selan {
356*60552Selan 	errno = 0;
357*60552Selan 	if (cpp_argindex >= ARGUMENTS-1)
358*60552Selan 		LogFatal("Out of internal storage.", "");
359*60552Selan 	cpp_argv[ cpp_argindex++ ] = arg;
360*60552Selan 	cpp_argv[ cpp_argindex ] = NULL;
361*60552Selan }
362*60552Selan 
363*60552Selan SetOpts(argc, argv)
364*60552Selan 	int	argc;
365*60552Selan 	char	**argv;
366*60552Selan {
367*60552Selan 	errno = 0;
368*60552Selan 	/*
369*60552Selan 	 * Now gather the arguments for make
370*60552Selan 	 */
371*60552Selan 	for(argc--, argv++; argc; argc--, argv++) {
372*60552Selan 	    /*
373*60552Selan 	     * We intercept these flags.
374*60552Selan 	     */
375*60552Selan 	    if (argv[0][0] == '-') {
376*60552Selan 		if (argv[0][1] == 'D') {
377*60552Selan 		    AddCppArg(argv[0]);
378*60552Selan 		} else if (argv[0][1] == 'I') {
379*60552Selan 		    AddCppArg(argv[0]);
380*60552Selan 		} else if (argv[0][1] == 'f') {
381*60552Selan 		    if (argv[0][2])
382*60552Selan 			Imakefile = argv[0]+2;
383*60552Selan 		    else {
384*60552Selan 			argc--, argv++;
385*60552Selan 			if (! argc)
386*60552Selan 			    LogFatal("No description arg after -f flag\n", "");
387*60552Selan 			Imakefile = argv[0];
388*60552Selan 		    }
389*60552Selan 		} else if (argv[0][1] == 's') {
390*60552Selan 		    if (argv[0][2])
391*60552Selan 			Makefile = ((argv[0][2] == '-') && !argv[0][3]) ?
392*60552Selan 			    NULL : argv[0]+2;
393*60552Selan 		    else {
394*60552Selan 			argc--, argv++;
395*60552Selan 			if (!argc)
396*60552Selan 			    LogFatal("No description arg after -s flag\n", "");
397*60552Selan 			Makefile = ((argv[0][0] == '-') && !argv[0][1]) ?
398*60552Selan 			    NULL : argv[0];
399*60552Selan 		    }
400*60552Selan 		    show = TRUE;
401*60552Selan 		} else if (argv[0][1] == 'e') {
402*60552Selan 		   Makefile = (argv[0][2] ? argv[0]+2 : NULL);
403*60552Selan 		   show = FALSE;
404*60552Selan 		} else if (argv[0][1] == 'T') {
405*60552Selan 		    if (argv[0][2])
406*60552Selan 			Template = argv[0]+2;
407*60552Selan 		    else {
408*60552Selan 			argc--, argv++;
409*60552Selan 			if (! argc)
410*60552Selan 			    LogFatal("No description arg after -T flag\n", "");
411*60552Selan 			Template = argv[0];
412*60552Selan 		    }
413*60552Selan 		} else if (argv[0][1] == 'v') {
414*60552Selan 		    verbose = TRUE;
415*60552Selan 		} else
416*60552Selan 		    AddMakeArg(argv[0]);
417*60552Selan 	    } else
418*60552Selan 		AddMakeArg(argv[0]);
419*60552Selan 	}
420*60552Selan }
421*60552Selan 
422*60552Selan char *FindImakefile(Imakefile)
423*60552Selan 	char	*Imakefile;
424*60552Selan {
425*60552Selan 	int	fd;
426*60552Selan 
427*60552Selan 	if (Imakefile) {
428*60552Selan 		if ((fd = open(Imakefile, O_RDONLY)) < 0)
429*60552Selan 			LogFatal("Cannot open %s.", Imakefile);
430*60552Selan 	} else {
431*60552Selan 		if ((fd = open("Imakefile", O_RDONLY)) < 0)
432*60552Selan 			if ((fd = open("imakefile", O_RDONLY)) < 0)
433*60552Selan 				LogFatal("No description file.", "");
434*60552Selan 			else
435*60552Selan 				Imakefile = "imakefile";
436*60552Selan 		else
437*60552Selan 			Imakefile = "Imakefile";
438*60552Selan 	}
439*60552Selan 	close (fd);
440*60552Selan 	return(Imakefile);
441*60552Selan }
442*60552Selan 
443*60552Selan LogFatalI(s, i)
444*60552Selan 	char *s;
445*60552Selan 	int i;
446*60552Selan {
447*60552Selan 	/*NOSTRICT*/
448*60552Selan 	LogFatal(s, (char *)i);
449*60552Selan }
450*60552Selan 
451*60552Selan LogFatal(x0,x1)
452*60552Selan 	char *x0, *x1;
453*60552Selan {
454*60552Selan 	extern char	*sys_errlist[];
455*60552Selan 	static boolean	entered = FALSE;
456*60552Selan 
457*60552Selan 	if (entered)
458*60552Selan 		return;
459*60552Selan 	entered = TRUE;
460*60552Selan 
461*60552Selan 	fprintf(stderr, "%s: ", program);
462*60552Selan 	if (errno)
463*60552Selan 		fprintf(stderr, "%s: ", sys_errlist[ errno ]);
464*60552Selan 	fprintf(stderr, x0,x1);
465*60552Selan 	fprintf(stderr, "  Stop.\n");
466*60552Selan 	wrapup();
467*60552Selan 	exit(1);
468*60552Selan }
469*60552Selan 
470*60552Selan showargs(argv)
471*60552Selan 	char	**argv;
472*60552Selan {
473*60552Selan 	for (; *argv; argv++)
474*60552Selan 		fprintf(stderr, "%s ", *argv);
475*60552Selan 	fprintf(stderr, "\n");
476*60552Selan }
477*60552Selan 
478*60552Selan cppit(Imakefile, template, outfd, outfname)
479*60552Selan 	char	*Imakefile;
480*60552Selan 	char	*template;
481*60552Selan 	FILE	*outfd;
482*60552Selan 	char	*outfname;
483*60552Selan {
484*60552Selan 	FILE	*pipeFile;
485*60552Selan 	int	pid, pipefd[2];
486*60552Selan 	waitType	status;
487*60552Selan 	char	*cleanedImakefile;
488*60552Selan 
489*60552Selan 	/*
490*60552Selan 	 * Get a pipe.
491*60552Selan 	 */
492*60552Selan 	if (pipe(pipefd) < 0)
493*60552Selan 		LogFatal("Cannot make a pipe.", "");
494*60552Selan 
495*60552Selan 	/*
496*60552Selan 	 * Fork and exec cpp
497*60552Selan 	 */
498*60552Selan 	pid = fork();
499*60552Selan 	if (pid < 0)
500*60552Selan 		LogFatal("Cannot fork.", "");
501*60552Selan 	if (pid) {	/* parent */
502*60552Selan 		close(pipefd[0]);
503*60552Selan 		cleanedImakefile = CleanCppInput(Imakefile);
504*60552Selan 		if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
505*60552Selan 			LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
506*60552Selan 		fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
507*60552Selan 			template);
508*60552Selan 		fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t<%s>\n",
509*60552Selan 			cleanedImakefile);
510*60552Selan 		fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
511*60552Selan 		fclose(pipeFile);
512*60552Selan 		while (wait(&status) > 0) {
513*60552Selan 			errno = 0;
514*60552Selan 			if (WIFSIGNALED(status))
515*60552Selan 				LogFatalI("Signal %d.", waitSig(status));
516*60552Selan 			if (WIFEXITED(status) && waitCode(status))
517*60552Selan 				LogFatalI("Exit code %d.", waitCode(status));
518*60552Selan 		}
519*60552Selan 		CleanCppOutput(outfd, outfname);
520*60552Selan 	} else {	/* child... dup and exec cpp */
521*60552Selan 		if (verbose)
522*60552Selan 			showargs(cpp_argv);
523*60552Selan 		dup2(pipefd[0], 0);
524*60552Selan 		dup2(fileno(outfd), 1);
525*60552Selan 		close(pipefd[1]);
526*60552Selan 		execv(cpp, cpp_argv);
527*60552Selan 		LogFatal("Cannot exec %s.", cpp);
528*60552Selan 	}
529*60552Selan }
530*60552Selan 
531*60552Selan makeit()
532*60552Selan {
533*60552Selan 	int	pid;
534*60552Selan 	waitType	status;
535*60552Selan 
536*60552Selan 	/*
537*60552Selan 	 * Fork and exec make
538*60552Selan 	 */
539*60552Selan 	pid = fork();
540*60552Selan 	if (pid < 0)
541*60552Selan 		LogFatal("Cannot fork.", "");
542*60552Selan 	if (pid) {	/* parent... simply wait */
543*60552Selan 		while (wait(&status) > 0) {
544*60552Selan 			errno = 0;
545*60552Selan 			if (WIFSIGNALED(status))
546*60552Selan 				LogFatalI("Signal %d.", waitSig(status));
547*60552Selan 			if (WIFEXITED(status) && waitCode(status))
548*60552Selan 				LogFatalI("Exit code %d.", waitCode(status));
549*60552Selan 		}
550*60552Selan 	} else {	/* child... dup and exec cpp */
551*60552Selan 		if (verbose)
552*60552Selan 			showargs(make_argv);
553*60552Selan 		if (make)
554*60552Selan 			execv(make, make_argv);
555*60552Selan 		else
556*60552Selan 			execvp("make", make_argv);
557*60552Selan 		LogFatal("Cannot exec %s.", make);
558*60552Selan 	}
559*60552Selan }
560*60552Selan 
561*60552Selan char *CleanCppInput(Imakefile)
562*60552Selan 	char	*Imakefile;
563*60552Selan {
564*60552Selan 	FILE	*outFile = NULL;
565*60552Selan 	int	infd;
566*60552Selan 	char	*buf,		/* buffer for file content */
567*60552Selan 		*pbuf,		/* walking pointer to buf */
568*60552Selan 		*punwritten,	/* pointer to unwritten portion of buf */
569*60552Selan 		*cleanedImakefile = Imakefile,	/* return value */
570*60552Selan 		*ptoken,	/* pointer to # token */
571*60552Selan 		*pend,		/* pointer to end of # token */
572*60552Selan 		savec;		/* temporary character holder */
573*60552Selan 	struct stat	st;
574*60552Selan 
575*60552Selan 	/*
576*60552Selan 	 * grab the entire file.
577*60552Selan 	 */
578*60552Selan 	if ((infd = open(Imakefile, O_RDONLY)) < 0)
579*60552Selan 		LogFatal("Cannot open %s for input.", Imakefile);
580*60552Selan 	fstat(infd, &st);
581*60552Selan 	buf = Emalloc(st.st_size+1);
582*60552Selan 	if (read(infd, buf, st.st_size) != st.st_size)
583*60552Selan 		LogFatal("Cannot read all of %s:", Imakefile);
584*60552Selan 	close(infd);
585*60552Selan 	buf[ st.st_size ] = '\0';
586*60552Selan 
587*60552Selan 	punwritten = pbuf = buf;
588*60552Selan 	while (*pbuf) {
589*60552Selan 	    /* pad make comments for cpp */
590*60552Selan 	    if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
591*60552Selan 
592*60552Selan 		ptoken = pbuf+1;
593*60552Selan 		while (*ptoken == ' ' || *ptoken == '\t')
594*60552Selan 			ptoken++;
595*60552Selan 		pend = ptoken;
596*60552Selan 		while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
597*60552Selan 			pend++;
598*60552Selan 		savec = *pend;
599*60552Selan 		*pend = '\0';
600*60552Selan 		if (strcmp(ptoken, "include")
601*60552Selan 		 && strcmp(ptoken, "define")
602*60552Selan 		 && strcmp(ptoken, "undef")
603*60552Selan 		 && strcmp(ptoken, "ifdef")
604*60552Selan 		 && strcmp(ptoken, "ifndef")
605*60552Selan 		 && strcmp(ptoken, "else")
606*60552Selan 		 && strcmp(ptoken, "endif")
607*60552Selan 		 && strcmp(ptoken, "if")) {
608*60552Selan 		    if (outFile == NULL) {
609*60552Selan 			tmpImakefile = Strdup(tmpImakefile);
610*60552Selan 			(void) mktemp(tmpImakefile);
611*60552Selan 			cleanedImakefile = tmpImakefile;
612*60552Selan 			outFile = fopen(tmpImakefile, "w");
613*60552Selan 			if (outFile == NULL)
614*60552Selan 			    LogFatal("Cannot open %s for write.\n",
615*60552Selan 				tmpImakefile);
616*60552Selan 		    }
617*60552Selan 		    fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
618*60552Selan 		    fputs("/**/", outFile);
619*60552Selan 		    punwritten = pbuf;
620*60552Selan 		}
621*60552Selan 		*pend = savec;
622*60552Selan 	    }
623*60552Selan 	    pbuf++;
624*60552Selan 	}
625*60552Selan 	if (outFile) {
626*60552Selan 	    fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
627*60552Selan 	    fclose(outFile); /* also closes the pipe */
628*60552Selan 	}
629*60552Selan 
630*60552Selan 	return(cleanedImakefile);
631*60552Selan }
632*60552Selan 
633*60552Selan CleanCppOutput(tmpfd, tmpfname)
634*60552Selan 	FILE	*tmpfd;
635*60552Selan 	char	*tmpfname;
636*60552Selan {
637*60552Selan 	char	*input;
638*60552Selan 	int	blankline = 0;
639*60552Selan 
640*60552Selan 	while(input = ReadLine(tmpfd, tmpfname)) {
641*60552Selan 		if (isempty(input)) {
642*60552Selan 			if (blankline++)
643*60552Selan 				continue;
644*60552Selan 			KludgeResetRule();
645*60552Selan 		} else {
646*60552Selan 			blankline = 0;
647*60552Selan 			KludgeOutputLine(&input);
648*60552Selan 			fputs(input, tmpfd);
649*60552Selan 		}
650*60552Selan 		putc('\n', tmpfd);
651*60552Selan 	}
652*60552Selan 	fflush(tmpfd);
653*60552Selan #ifdef NFS_STDOUT_BUG
654*60552Selan 	/*
655*60552Selan 	 * On some systems, NFS seems to leave a large number of nulls at
656*60552Selan 	 * the end of the file.  Ralph Swick says that this kludge makes the
657*60552Selan 	 * problem go away.
658*60552Selan 	 */
659*60552Selan 	ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
660*60552Selan #endif
661*60552Selan }
662*60552Selan 
663*60552Selan /*
664*60552Selan  * Determine of a line has nothing in it.  As a side effect, we trim white
665*60552Selan  * space from the end of the line.  Cpp magic cookies are also thrown away.
666*60552Selan  */
667*60552Selan isempty(line)
668*60552Selan 	char	*line;
669*60552Selan {
670*60552Selan 	char	*pend;
671*60552Selan 
672*60552Selan 	/*
673*60552Selan 	 * Check for lines of the form
674*60552Selan 	 *	# n "...
675*60552Selan 	 * or
676*60552Selan 	 *	# line n "...
677*60552Selan 	 */
678*60552Selan 	if (*line == '#') {
679*60552Selan 		pend = line+1;
680*60552Selan 		if (*pend == ' ')
681*60552Selan 			pend++;
682*60552Selan 		if (strncmp(pend, "line ", 5) == 0)
683*60552Selan 			pend += 5;
684*60552Selan 		if (isdigit(*pend)) {
685*60552Selan 			while (isdigit(*pend))
686*60552Selan 				pend++;
687*60552Selan 			if (*pend++ == ' ' && *pend == '"')
688*60552Selan 				return(TRUE);
689*60552Selan 		}
690*60552Selan 	}
691*60552Selan 
692*60552Selan 	/*
693*60552Selan 	 * Find the end of the line and then walk back.
694*60552Selan 	 */
695*60552Selan 	for (pend=line; *pend; pend++) ;
696*60552Selan 
697*60552Selan 	pend--;
698*60552Selan 	while (pend >= line && (*pend == ' ' || *pend == '\t'))
699*60552Selan 		pend--;
700*60552Selan 	*++pend = '\0';
701*60552Selan 	return (*line == '\0');
702*60552Selan }
703*60552Selan 
704*60552Selan /*ARGSUSED*/
705*60552Selan char *ReadLine(tmpfd, tmpfname)
706*60552Selan 	FILE	*tmpfd;
707*60552Selan 	char	*tmpfname;
708*60552Selan {
709*60552Selan 	static boolean	initialized = FALSE;
710*60552Selan 	static char	*buf, *pline, *end;
711*60552Selan 	char	*p1, *p2;
712*60552Selan 
713*60552Selan 	if (! initialized) {
714*60552Selan 		int	total_red;
715*60552Selan 		struct stat	st;
716*60552Selan 
717*60552Selan 		/*
718*60552Selan 		 * Slurp it all up.
719*60552Selan 		 */
720*60552Selan 		fseek(tmpfd, 0, 0);
721*60552Selan 		fstat(fileno(tmpfd), &st);
722*60552Selan 		pline = buf = Emalloc(st.st_size+1);
723*60552Selan 		total_red = read(fileno(tmpfd), buf, st.st_size);
724*60552Selan 		if (total_red != st.st_size)
725*60552Selan 			LogFatal("cannot read %s\n", tmpMakefile);
726*60552Selan 		end = buf + st.st_size;
727*60552Selan 		*end = '\0';
728*60552Selan 		lseek(fileno(tmpfd), 0, 0);
729*60552Selan #ifdef SYSV
730*60552Selan 		freopen(tmpfname, "w+", tmpfd);
731*60552Selan #else	/* !SYSV */
732*60552Selan 		ftruncate(fileno(tmpfd), 0);
733*60552Selan #endif	/* !SYSV */
734*60552Selan 		initialized = TRUE;
735*60552Selan 	    fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
736*60552Selan 	    fprintf (tmpfd, "# %s\n",
737*60552Selan 		"$XConsortium: imake.c,v 1.65 91/07/25 17:50:17 rws Exp $");
738*60552Selan 
739*60552Selan #ifdef FIXUP_CPP_WHITESPACE
740*60552Selan 	    {
741*60552Selan 		static char *cpp_warning[] = {
742*60552Selan "#",
743*60552Selan "# The cpp used on this machine replaces all newlines and multiple tabs and",
744*60552Selan "# spaces in a macro expansion with a single space.  Imake tries to compensate",
745*60552Selan "# for this, but is not always successful.",
746*60552Selan "#",
747*60552Selan NULL };
748*60552Selan 		char **cpp;
749*60552Selan 
750*60552Selan 		for (cpp = cpp_warning; *cpp; cpp++) {
751*60552Selan 		    fprintf (tmpfd, "%s\n", *cpp);
752*60552Selan 		}
753*60552Selan 	    }
754*60552Selan #endif /* FIXUP_CPP_WHITESPACE */
755*60552Selan 	}
756*60552Selan 
757*60552Selan 	for (p1 = pline; p1 < end; p1++) {
758*60552Selan 		if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
759*60552Selan 			*p1++ = '\0';
760*60552Selan 			p1++; /* skip over second @ */
761*60552Selan 			break;
762*60552Selan 		}
763*60552Selan 		else if (*p1 == '\n') { /* real EOL */
764*60552Selan 			*p1++ = '\0';
765*60552Selan 			break;
766*60552Selan 		}
767*60552Selan 	}
768*60552Selan 
769*60552Selan 	/*
770*60552Selan 	 * return NULL at the end of the file.
771*60552Selan 	 */
772*60552Selan 	p2 = (pline == p1 ? NULL : pline);
773*60552Selan 	pline = p1;
774*60552Selan 	return(p2);
775*60552Selan }
776*60552Selan 
777*60552Selan writetmpfile(fd, buf, cnt)
778*60552Selan 	FILE	*fd;
779*60552Selan 	int	cnt;
780*60552Selan 	char	*buf;
781*60552Selan {
782*60552Selan 	errno = 0;
783*60552Selan 	if (fwrite(buf, cnt, 1, fd) != 1)
784*60552Selan 		LogFatal("Cannot write to %s.", tmpMakefile);
785*60552Selan }
786*60552Selan 
787*60552Selan char *Emalloc(size)
788*60552Selan 	int	size;
789*60552Selan {
790*60552Selan 	char	*p;
791*60552Selan 
792*60552Selan 	if ((p = malloc(size)) == NULL)
793*60552Selan 		LogFatalI("Cannot allocate %d bytes\n", size);
794*60552Selan 	return(p);
795*60552Selan }
796*60552Selan 
797*60552Selan #ifdef FIXUP_CPP_WHITESPACE
798*60552Selan KludgeOutputLine(pline)
799*60552Selan 	char	**pline;
800*60552Selan {
801*60552Selan 	char	*p = *pline;
802*60552Selan 
803*60552Selan 	switch (*p) {
804*60552Selan 	    case '#':	/*Comment - ignore*/
805*60552Selan 		break;
806*60552Selan 	    case '\t':	/*Already tabbed - ignore it*/
807*60552Selan 	    	break;
808*60552Selan 	    case ' ':	/*May need a tab*/
809*60552Selan 	    default:
810*60552Selan 		for (; *p; p++) if (p[0] == ':' &&
811*60552Selan 				    p > *pline && p[-1] != '\\') {
812*60552Selan 		    if (**pline == ' ')
813*60552Selan 			(*pline)++;
814*60552Selan 		    InRule = TRUE;
815*60552Selan 		    break;
816*60552Selan 		}
817*60552Selan 		if (InRule && **pline == ' ')
818*60552Selan 		    **pline = '\t';
819*60552Selan 		break;
820*60552Selan 	}
821*60552Selan }
822*60552Selan 
823*60552Selan KludgeResetRule()
824*60552Selan {
825*60552Selan 	InRule = FALSE;
826*60552Selan }
827*60552Selan #endif /* FIXUP_CPP_WHITESPACE */
828*60552Selan 
829*60552Selan char *Strdup(cp)
830*60552Selan 	register char *cp;
831*60552Selan {
832*60552Selan 	register char *new = Emalloc(strlen(cp) + 1);
833*60552Selan 
834*60552Selan 	strcpy(new, cp);
835*60552Selan 	return new;
836*60552Selan }
837