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