|
|
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:
1.1.1.2 ! root 100: /*
1.1 root 101: #ifdef sun
102: #define REDUCED_TO_ASCII_SPACE
103: int InRule = FALSE;
104: #endif
1.1.1.2 ! root 105: */
1.1 root 106:
107: /*
108: * Some versions of cpp reduce all tabs in macro expansion to a single
109: * space. In addition, the escaped newline may be replaced with a
110: * space instead of being deleted. Blech.
111: */
112: #ifndef REDUCED_TO_ASCII_SPACE
113: #define KludgeOutputLine(arg)
114: #define KludgeResetRule()
115: #endif
116:
117: typedef u_char boolean;
118:
119: #ifndef apollo
120: char *cpp = "/lib/cpp";
121: #else apollo
122: char *cpp = "/usr/lib/cpp";
123: #endif apollo
124:
1.1.1.2 ! root 125: char *tmpMakefile = "/usr/tmp/tm.XXXXXX";
! 126: char *tmpImakefile = "/usr/tmp/tim.XXXXXX";
1.1 root 127: char *make_argv[ ARGUMENTS ] = { "make" };
128: char *cpp_argv[ ARGUMENTS ] = {
129: "cpp",
130: "-I.",
131: #ifdef unix
132: "-Uunix",
133: #endif unix
134: };
135: int make_argindex = 1;
136: int cpp_argindex = 3;
137: char *make = NULL;
138: char *Imakefile = NULL;
139: char *Makefile = NULL;
140: char *Template = "Imake.template";
141: char *program;
142: char *FindImakefile();
143: char *ReadLine();
144: char *CleanCppInput();
145: boolean verbose = FALSE;
146: boolean show = FALSE;
147: extern int errno;
148: extern char *Emalloc();
149: extern char *realloc();
150: extern char *getenv();
151: extern char *mktemp();
152:
153: main(argc, argv)
154: int argc;
155: char **argv;
156: {
157: FILE *tmpfd;
158: char makeMacro[ BUFSIZ ];
159: char makefileMacro[ BUFSIZ ];
160:
161: init();
162: SetOpts(argc, argv);
163:
164: AddCppArg("-I/usr/lib/local/imake.includes");
165:
166: Imakefile = FindImakefile(Imakefile);
167: if (Makefile)
168: tmpMakefile = Makefile;
169: else
170: tmpMakefile = mktemp(tmpMakefile);
171: AddMakeArg("-f");
172: AddMakeArg( tmpMakefile );
173: sprintf(makeMacro, "MAKE=%s", program);
174: AddMakeArg( makeMacro );
175: sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
176: AddMakeArg( makefileMacro );
177:
178: if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
179: LogFatal("Cannot create temporary file %s.", tmpMakefile);
180:
181: cppit(Imakefile, Template, tmpfd);
182:
183: if (show) {
184: if (Makefile == NULL)
185: showit(tmpfd);
186: } else
187: makeit();
188: wrapup();
189: exit(0);
190: }
191:
192: showit(fd)
193: FILE *fd;
194: {
195: char buf[ BUFSIZ ];
196: int red;
197:
198: fseek(fd, 0, 0);
199: while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
200: fwrite(buf, red, 1, stdout);
201: if (red < 0)
202: LogFatal("Cannot write stdout.");
203: }
204:
205: wrapup()
206: {
207: if (tmpMakefile != Makefile)
208: unlink(tmpMakefile);
209: unlink(tmpImakefile);
210: }
211:
212: catch(sig)
213: int sig;
214: {
215: errno = 0;
216: LogFatal("Signal %d.", sig);
217: }
218:
219: /*
220: * Initialize some variables.
221: */
222: init()
223: {
224: char *p;
225:
226: /*
227: * See if the standard include directory is different than
228: * the default. Or if cpp is not the default. Or if the make
229: * found by the PATH variable is not the default.
230: */
231: if (p = getenv("IMAKEINCLUDE")) {
232: if (*p != '-' || *(p+1) != 'I')
233: LogFatal("Environment var IMAKEINCLUDE %s\n",
234: "must begin with -I");
235: AddCppArg(p);
236: for (; *p; p++)
237: if (*p == ' ') {
238: *p++ = '\0';
239: AddCppArg(p);
240: }
241: }
242: if (p = getenv("IMAKECPP"))
243: cpp = p;
244: if (p = getenv("IMAKEMAKE"))
245: make = p;
246:
247: if (signal(SIGINT, SIG_IGN) != SIG_IGN)
248: signal(SIGINT, catch);
249: }
250:
251: AddMakeArg(arg)
252: char *arg;
253: {
254: errno = 0;
255: if (make_argindex >= ARGUMENTS-1)
256: LogFatal("Out of internal storage.");
257: make_argv[ make_argindex++ ] = arg;
258: make_argv[ make_argindex ] = NULL;
259: }
260:
261: AddCppArg(arg)
262: char *arg;
263: {
264: errno = 0;
265: if (cpp_argindex >= ARGUMENTS-1)
266: LogFatal("Out of internal storage.");
267: cpp_argv[ cpp_argindex++ ] = arg;
268: cpp_argv[ cpp_argindex ] = NULL;
269: }
270:
271: SetOpts(argc, argv)
272: int argc;
273: char **argv;
274: {
275: errno = 0;
276: /*
277: * Now gather the arguments for make
278: */
279: program = argv[0];
280: for(argc--, argv++; argc; argc--, argv++) {
281: /*
282: * We intercept these flags.
283: */
284: if (argv[0][0] == '-') {
285: if (argv[0][1] == 'D') {
286: AddCppArg(argv[0]);
287: } else if (argv[0][1] == 'I') {
288: AddCppArg(argv[0]);
289: } else if (argv[0][1] == 'f') {
290: if (argv[0][2])
291: Imakefile = argv[0]+2;
292: else {
293: argc--, argv++;
294: if (! argc)
295: LogFatal("No description arg after -f flag\n");
296: Imakefile = argv[0];
297: }
298: } else if (argv[0][1] == 's') {
299: if (argv[0][2])
300: Makefile = argv[0]+2;
301: else if (argc > 1 && argv[1][0] != '-') {
302: argc--, argv++;
303: Makefile = argv[0];
304: }
305: show = TRUE;
306: } else if (argv[0][1] == 'T') {
307: if (argv[0][2])
308: Template = argv[0]+2;
309: else {
310: argc--, argv++;
311: if (! argc)
312: LogFatal("No description arg after -T flag\n");
313: Template = argv[0];
314: }
315: } else if (argv[0][1] == 'v') {
316: verbose = TRUE;
317: } else
318: AddMakeArg(argv[0]);
319: } else
320: AddMakeArg(argv[0]);
321: }
322: }
323:
324: char *FindImakefile(Imakefile)
325: char *Imakefile;
326: {
327: int fd;
328:
329: if (Imakefile) {
1.1.1.2 ! root 330: if ((fd = open(Imakefile, 0)) < 0)
1.1 root 331: LogFatal("Cannot open %s.", Imakefile);
332: } else {
1.1.1.2 ! root 333: if ((fd = open("Imakefile", 0)) < 0)
! 334: if ((fd = open("imakefile", 0)) < 0)
1.1 root 335: LogFatal("No description file.");
336: else
337: Imakefile = "imakefile";
338: else
339: Imakefile = "Imakefile";
340: }
341: close (fd);
342: return(Imakefile);
343: }
344:
345: LogFatal(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)
346: {
347: extern char *sys_errlist[];
348: static boolean entered = FALSE;
349:
350: if (entered)
351: return;
352: entered = TRUE;
353:
354: fprintf(stderr, "%s: ", program);
355: if (errno)
356: fprintf(stderr, "%s: ", sys_errlist[ errno ]);
357: fprintf(stderr, x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
358: fprintf(stderr, " Stop.\n");
359: wrapup();
360: exit(1);
361: }
362:
363: showargs(argv)
364: char **argv;
365: {
366: for (; *argv; argv++)
367: fprintf(stderr, "%s ", *argv);
368: fprintf(stderr, "\n");
369: }
370:
371: cppit(Imakefile, template, outfd)
372: char *Imakefile;
373: char *template;
374: FILE *outfd;
375: {
376: FILE *pipeFile;
377: int pid, pipefd[2];
378: union wait status;
379: char *cleanedImakefile;
380:
381: /*
382: * Get a pipe.
383: */
384: if (pipe(pipefd) < 0)
385: LogFatal("Cannot make a pipe.");
386:
387: /*
388: * Fork and exec cpp
389: */
390: pid = vfork();
391: if (pid < 0)
392: LogFatal("Cannot fork.");
393: if (pid) { /* parent */
394: close(pipefd[0]);
395: cleanedImakefile = CleanCppInput(Imakefile);
396: if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
397: LogFatal("Cannot fdopen fd %d for output.", outfd);
398: fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
399: template);
400: fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t\"%s\"\n",
401: cleanedImakefile);
402: fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
403: fclose(pipeFile);
404: while (wait(&status) > 0) {
405: errno = 0;
406: if (status.w_termsig)
407: LogFatal("Signal %d.", status.w_termsig);
408: if (status.w_retcode)
409: LogFatal("Exit code %d.", status.w_retcode);
410: }
411: CleanCppOutput(outfd);
412: } else { /* child... dup and exec cpp */
413: if (verbose)
414: showargs(cpp_argv);
415: dup2(pipefd[0], 0);
416: dup2(fileno(outfd), 1);
417: close(pipefd[1]);
418: execv(cpp, cpp_argv);
419: LogFatal("Cannot exec %s.", cpp);
420: }
421: }
422:
423: makeit()
424: {
425: int pid;
426: union wait status;
427:
428: /*
429: * Fork and exec make
430: */
431: pid = vfork();
432: if (pid < 0)
433: LogFatal("Cannot fork.");
434: if (pid) { /* parent... simply wait */
435: while (wait(&status) > 0) {
436: errno = 0;
437: if (status.w_termsig)
438: LogFatal("Signal %d.", status.w_termsig);
439: if (status.w_retcode)
440: LogFatal("Exit code %d.", status.w_retcode);
441: }
442: } else { /* child... dup and exec cpp */
443: if (verbose)
444: showargs(make_argv);
445: if (make)
446: execv(make, make_argv);
447: else
448: execvp("make", make_argv);
449: LogFatal("Cannot exec %s.", cpp);
450: }
451: }
452:
453: char *CleanCppInput(Imakefile)
454: char *Imakefile;
455: {
456: FILE *outFile = NULL;
457: int infd, got;
458: char *buf, /* buffer for file content */
459: *pbuf, /* walking pointer to buf */
460: *punwritten, /* pointer to unwritten portion of buf */
461: *cleanedImakefile = Imakefile, /* return value */
462: *ptoken, /* pointer to # token */
463: *pend, /* pointer to end of # token */
464: savec; /* temporary character holder */
465: struct stat st;
466:
467: /*
468: * grab the entire file.
469: */
1.1.1.2 ! root 470: if ((infd = open(Imakefile, 0)) < 0)
1.1 root 471: LogFatal("Cannot open %s for input.", Imakefile);
472: fstat(infd, &st);
473: buf = Emalloc(st.st_size+1);
474: if ((got = read(infd, buf, st.st_size)) != st.st_size)
475: LogFatal("Cannot read all of %s: want %d, got %d\n",
476: Imakefile, st.st_size, got);
477: close(infd);
478: buf[ st.st_size ] = '\0';
479:
480: punwritten = pbuf = buf;
481: while (*pbuf) {
482: /* pad make comments for cpp */
483: if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
484:
485: ptoken = pbuf+1;
486: while (*ptoken == ' ' || *ptoken == '\t')
487: ptoken++;
488: pend = ptoken;
489: while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
490: pend++;
491: savec = *pend;
492: *pend = '\0';
493: if (strcmp(ptoken, "include")
494: && strcmp(ptoken, "define")
495: && strcmp(ptoken, "undef")
496: && strcmp(ptoken, "ifdef")
497: && strcmp(ptoken, "ifndef")
498: && strcmp(ptoken, "else")
499: && strcmp(ptoken, "endif")
500: && strcmp(ptoken, "if")) {
501: if (outFile == NULL) {
502: tmpImakefile = mktemp(tmpImakefile);
503: cleanedImakefile = tmpImakefile;
504: outFile = fopen(tmpImakefile, "w");
505: if (outFile == NULL)
506: LogFatal("Cannot open %s for write.\n",
507: tmpImakefile);
508: }
509: fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
510: fputs("/**/", outFile);
511: punwritten = pbuf;
512: }
513: *pend = savec;
514: }
515: pbuf++;
516: }
517: if (outFile) {
518: fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
519: fclose(outFile); /* also closes the pipe */
520: }
521:
522: return(cleanedImakefile);
523: }
524:
525: CleanCppOutput(tmpfd)
526: FILE *tmpfd;
527: {
528: char *input;
529: int blankline = 0;
530:
531: while(input = ReadLine(tmpfd)) {
532: if (isempty(input)) {
533: if (blankline++)
534: continue;
535: KludgeResetRule();
536: } else {
537: blankline = 0;
538: KludgeOutputLine(&input);
539: fputs(input, tmpfd);
540: }
541: putc('\n', tmpfd);
542: }
543: fflush(tmpfd);
544: }
545:
546: /*
547: * Determine of a line has nothing in it. As a side effect, we trim white
548: * space from the end of the line. Cpp magic cookies are also thrown away.
549: */
550: isempty(line)
551: char *line;
552: {
553: char *pend;
554:
555: /*
556: * Check for lines of the form
557: * # n "...
558: * or
559: * # line n "...
560: */
561: if (*line == '#') {
562: pend = line+1;
563: if (*pend == ' ')
564: pend++;
565: if (strncmp(pend, "line ", 5) == 0)
566: pend += 5;
567: if (isdigit(*pend)) {
568: while (isdigit(*pend))
569: pend++;
570: if (*pend++ == ' ' && *pend == '"')
571: return(TRUE);
572: }
573: }
574:
575: /*
576: * Find the end of the line and then walk back.
577: */
578: for (pend=line; *pend; pend++) ;
579:
580: pend--;
581: while (pend >= line && (*pend == ' ' || *pend == '\t'))
582: pend--;
583: *++pend = '\0';
584: return (*line == '\0');
585: }
586:
587: char *ReadLine(tmpfd)
588: FILE *tmpfd;
589: {
590: static boolean initialized = FALSE;
591: static char *buf, *pline, *end;
592: char *p1, *p2;
593:
594: if (! initialized) {
595: int total_red;
596: struct stat st;
597:
598: /*
599: * Slurp it all up.
600: */
601: fseek(tmpfd, 0, 0);
602: fstat(fileno(tmpfd), &st);
603: pline = buf = Emalloc(st.st_size+1);
604: total_red = read(fileno(tmpfd), buf, st.st_size);
605: if (total_red != st.st_size)
606: LogFatal("cannot read %s\n", tmpMakefile);
607: end = buf + st.st_size;
608: *end = '\0';
1.1.1.2 ! root 609: unlink(fileno(tmpfd));
! 610: close(fileno(tmpfd));
! 611: fileno(tmpfd) = creat(tmpMakefile, 644);
! 612: if (fileno(tmpfd) < 0)
! 613: LogFatal("Cannot create temporary file %s.", tmpMakefile);
1.1 root 614: initialized = TRUE;
615: #ifdef REDUCED_TO_ASCII_SPACE
616: fprintf(tmpfd, "#\n");
617: fprintf(tmpfd, "# Warning: the cpp used on this machine replaces\n");
618: fprintf(tmpfd, "# all newlines and multiple tabs/spaces in a macro\n");
619: fprintf(tmpfd, "# expansion with a single space. Imake tries to\n");
620: fprintf(tmpfd, "# compensate for this, but is not always\n");
621: fprintf(tmpfd, "# successful.\n");
622: fprintf(tmpfd, "#\n");
623: #endif REDUCED_TO_ASCII_SPACE
624: }
625:
626: for (p1 = pline; p1 < end; p1++) {
627: if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
628: *p1++ = '\0';
629: p1++; /* skip over second @ */
630: break;
631: }
632: else if (*p1 == '\n') { /* real EOL */
633: *p1++ = '\0';
634: break;
635: }
636: }
637:
638: /*
639: * return NULL at the end of the file.
640: */
641: p2 = (pline == p1 ? NULL : pline);
642: pline = p1;
643: return(p2);
644: }
645:
646: writetmpfile(fd, buf, cnt)
647: FILE *fd;
648: int cnt;
649: char *buf;
650: {
651: errno = 0;
652: if (fwrite(buf, cnt, 1, fd) != 1)
653: LogFatal("Cannot write to %s.", tmpMakefile);
654: }
655:
656: char *Emalloc(size)
657: int size;
658: {
659: char *p, *malloc();
660:
661: if ((p = malloc(size)) == NULL)
662: LogFatal("Cannot allocate %d bytes\n", size);
663: return(p);
664: }
665:
666: #ifdef REDUCED_TO_ASCII_SPACE
667: KludgeOutputLine(pline)
668: char **pline;
669: {
670: char *p = *pline;
671:
672: switch (*p) {
673: case '#': /*Comment - ignore*/
674: break;
675: case '\t': /*Already tabbed - ignore it*/
676: break;
677: case ' ': /*May need a tab*/
678: default:
679: while (*p) if (*p++ == ':') {
680: if (**pline == ' ')
681: (*pline)++;
682: InRule = TRUE;
683: break;
684: }
685: if (InRule && **pline == ' ')
686: **pline = '\t';
687: break;
688: }
689: }
690:
691: KludgeResetRule()
692: {
693: InRule = FALSE;
694: }
695: #endif REDUCED_TO_ASCII_SPACE
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.