mkinit.c revision 1556
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static char copyright[] =
39"@(#) Copyright (c) 1991, 1993\n\
40	The Regents of the University of California.  All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44static char sccsid[] = "@(#)mkinit.c	8.1 (Berkeley) 5/31/93";
45#endif /* not lint */
46
47/*
48 * This program scans all the source files for code to handle various
49 * special events and combines this code into one file.  This (allegedly)
50 * improves the structure of the program since there is no need for
51 * anyone outside of a module to know that that module performs special
52 * operations on particular events.  The command is executed iff init.c
53 * is actually changed.
54 *
55 * Usage:  mkinit command sourcefile...
56 */
57
58
59#include <sys/cdefs.h>
60#include <sys/types.h>
61#include <stdio.h>
62#include <fcntl.h>
63#include <unistd.h>
64
65
66/*
67 * OUTFILE is the name of the output file.  Output is initially written
68 * to the file OUTTEMP, which is then moved to OUTFILE if OUTTEMP and
69 * OUTFILE are different.
70 */
71
72#define OUTFILE "init.c"
73#define OUTTEMP "init.c.new"
74#define OUTOBJ "init.o"
75
76
77/*
78 * A text structure is basicly just a string that grows as more characters
79 * are added onto the end of it.  It is implemented as a linked list of
80 * blocks of characters.  The routines addstr and addchar append a string
81 * or a single character, respectively, to a text structure.  Writetext
82 * writes the contents of a text structure to a file.
83 */
84
85#define BLOCKSIZE 512
86
87struct text {
88	char *nextc;
89	int nleft;
90	struct block *start;
91	struct block *last;
92};
93
94struct block {
95	struct block *next;
96	char text[BLOCKSIZE];
97};
98
99
100/*
101 * There is one event structure for each event that mkinit handles.
102 */
103
104struct event {
105	char *name;		/* name of event (e.g. INIT) */
106	char *routine;		/* name of routine called on event */
107	char *comment;		/* comment describing routine */
108	struct text code;		/* code for handling event */
109};
110
111
112char writer[] = "\
113/*\n\
114 * This file was generated by the mkinit program.\n\
115 */\n\
116\n";
117
118char init[] = "\
119/*\n\
120 * Initialization code.\n\
121 */\n";
122
123char reset[] = "\
124/*\n\
125 * This routine is called when an error or an interrupt occurs in an\n\
126 * interactive shell and control is returned to the main command loop.\n\
127 */\n";
128
129char shellproc[] = "\
130/*\n\
131 * This routine is called to initialize the shell to run a shell procedure.\n\
132 */\n";
133
134
135struct event event[] = {
136	{"INIT", "init", init},
137	{"RESET", "reset", reset},
138	{"SHELLPROC", "initshellproc", shellproc},
139	{NULL, NULL}
140};
141
142
143char *curfile;				/* current file */
144int linno;				/* current line */
145char *header_files[200];		/* list of header files */
146struct text defines;			/* #define statements */
147struct text decls;			/* declarations */
148int amiddecls;				/* for formatting */
149
150
151void readfile(), doevent(), doinclude(), dodecl(), output();
152void addstr(), addchar(), writetext();
153
154#define equal(s1, s2)	(strcmp(s1, s2) == 0)
155
156FILE *ckfopen();
157char *savestr();
158void *ckmalloc __P((int));
159void error();
160
161main(argc, argv)
162	char **argv;
163	{
164	char **ap;
165	int fd;
166	char c;
167
168	if (argc < 2)
169		error("Usage:  mkinit command file...");
170	header_files[0] = "\"shell.h\"";
171	header_files[1] = "\"mystring.h\"";
172	for (ap = argv + 2 ; *ap ; ap++)
173		readfile(*ap);
174	output();
175	if (file_changed()) {
176		unlink(OUTFILE);
177		link(OUTTEMP, OUTFILE);
178		unlink(OUTTEMP);
179	} else {
180		unlink(OUTTEMP);
181		if (touch(OUTOBJ))
182			exit(0);		/* no compilation necessary */
183	}
184	printf("%s\n", argv[1]);
185	execl("/bin/sh", "sh", "-c", argv[1], (char *)0);
186	error("Can't exec shell");
187}
188
189
190/*
191 * Parse an input file.
192 */
193
194void
195readfile(fname)
196	char *fname;
197	{
198	FILE *fp;
199	char line[1024];
200	struct event *ep;
201
202	fp = ckfopen(fname, "r");
203	curfile = fname;
204	linno = 0;
205	amiddecls = 0;
206	while (fgets(line, sizeof line, fp) != NULL) {
207		linno++;
208		for (ep = event ; ep->name ; ep++) {
209			if (line[0] == ep->name[0] && match(ep->name, line)) {
210				doevent(ep, fp, fname);
211				break;
212			}
213		}
214		if (line[0] == 'I' && match("INCLUDE", line))
215			doinclude(line);
216		if (line[0] == 'M' && match("MKINIT", line))
217			dodecl(line, fp);
218		if (line[0] == '#' && gooddefine(line))
219			addstr(line, &defines);
220	}
221	fclose(fp);
222}
223
224
225int
226match(name, line)
227	char *name;
228	char *line;
229	{
230	register char *p, *q;
231
232	p = name, q = line;
233	while (*p) {
234		if (*p++ != *q++)
235			return 0;
236	}
237	if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n')
238		return 0;
239	return 1;
240}
241
242
243int
244gooddefine(line)
245	char *line;
246	{
247	register char *p;
248
249	if (! match("#define", line))
250		return 0;			/* not a define */
251	p = line + 7;
252	while (*p == ' ' || *p == '\t')
253		p++;
254	while (*p != ' ' && *p != '\t') {
255		if (*p == '(')
256			return 0;		/* macro definition */
257		p++;
258	}
259	while (*p != '\n' && *p != '\0')
260		p++;
261	if (p[-1] == '\\')
262		return 0;			/* multi-line definition */
263	return 1;
264}
265
266
267void
268doevent(ep, fp, fname)
269	register struct event *ep;
270	FILE *fp;
271	char *fname;
272	{
273	char line[1024];
274	int indent;
275	char *p;
276
277	sprintf(line, "\n      /* from %s: */\n", fname);
278	addstr(line, &ep->code);
279	addstr("      {\n", &ep->code);
280	for (;;) {
281		linno++;
282		if (fgets(line, sizeof line, fp) == NULL)
283			error("Unexpected EOF");
284		if (equal(line, "}\n"))
285			break;
286		indent = 6;
287		for (p = line ; *p == '\t' ; p++)
288			indent += 8;
289		for ( ; *p == ' ' ; p++)
290			indent++;
291		if (*p == '\n' || *p == '#')
292			indent = 0;
293		while (indent >= 8) {
294			addchar('\t', &ep->code);
295			indent -= 8;
296		}
297		while (indent > 0) {
298			addchar(' ', &ep->code);
299			indent--;
300		}
301		addstr(p, &ep->code);
302	}
303	addstr("      }\n", &ep->code);
304}
305
306
307void
308doinclude(line)
309	char *line;
310	{
311	register char *p;
312	char *name;
313	register char **pp;
314
315	for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++);
316	if (*p == '\0')
317		error("Expecting '\"' or '<'");
318	name = p;
319	while (*p != ' ' && *p != '\t' && *p != '\n')
320		p++;
321	if (p[-1] != '"' && p[-1] != '>')
322		error("Missing terminator");
323	*p = '\0';
324
325	/* name now contains the name of the include file */
326	for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++);
327	if (*pp == NULL)
328		*pp = savestr(name);
329}
330
331
332void
333dodecl(line1, fp)
334	char *line1;
335	FILE *fp;
336	{
337	char line[1024];
338	register char *p, *q;
339
340	if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */
341		addchar('\n', &decls);
342		do {
343			linno++;
344			if (fgets(line, sizeof line, fp) == NULL)
345				error("Unterminated structure declaration");
346			addstr(line, &decls);
347		} while (line[0] != '}');
348		amiddecls = 0;
349	} else {
350		if (! amiddecls)
351			addchar('\n', &decls);
352		q = NULL;
353		for (p = line1 + 6 ; *p != '=' && *p != '/' ; p++);
354		if (*p == '=') {		/* eliminate initialization */
355			for (q = p ; *q && *q != ';' ; q++);
356			if (*q == '\0')
357				q = NULL;
358			else {
359				while (p[-1] == ' ')
360					p--;
361				*p = '\0';
362			}
363		}
364		addstr("extern", &decls);
365		addstr(line1 + 6, &decls);
366		if (q != NULL)
367			addstr(q, &decls);
368		amiddecls = 1;
369	}
370}
371
372
373
374/*
375 * Write the output to the file OUTTEMP.
376 */
377
378void
379output() {
380	FILE *fp;
381	char **pp;
382	struct event *ep;
383
384	fp = ckfopen(OUTTEMP, "w");
385	fputs(writer, fp);
386	for (pp = header_files ; *pp ; pp++)
387		fprintf(fp, "#include %s\n", *pp);
388	fputs("\n\n\n", fp);
389	writetext(&defines, fp);
390	fputs("\n\n", fp);
391	writetext(&decls, fp);
392	for (ep = event ; ep->name ; ep++) {
393		fputs("\n\n\n", fp);
394		fputs(ep->comment, fp);
395		fprintf(fp, "\nvoid\n%s() {\n", ep->routine);
396		writetext(&ep->code, fp);
397		fprintf(fp, "}\n");
398	}
399	fclose(fp);
400}
401
402
403/*
404 * Return true if the new output file is different from the old one.
405 */
406
407int
408file_changed() {
409	register FILE *f1, *f2;
410	register int c;
411
412	if ((f1 = fopen(OUTFILE, "r")) == NULL
413	 || (f2 = fopen(OUTTEMP, "r")) == NULL)
414		return 1;
415	while ((c = getc(f1)) == getc(f2)) {
416		if (c == EOF)
417			return 0;
418	}
419	return 1;
420}
421
422
423/*
424 * Touch a file.  Returns 0 on failure, 1 on success.
425 */
426
427int
428touch(file)
429	char *file;
430	{
431	int fd;
432	char c;
433
434	if ((fd = open(file, O_RDWR)) < 0)
435		return 0;
436	if (read(fd, &c, 1) != 1) {
437		close(fd);
438		return 0;
439	}
440	lseek(fd, (off_t)0, 0);
441	write(fd, &c, 1);
442	close(fd);
443	return 1;
444}
445
446
447
448/*
449 * A text structure is simply a block of text that is kept in memory.
450 * Addstr appends a string to the text struct, and addchar appends a single
451 * character.
452 */
453
454void
455addstr(s, text)
456	register char *s;
457	register struct text *text;
458	{
459	while (*s) {
460		if (--text->nleft < 0)
461			addchar(*s++, text);
462		else
463			*text->nextc++ = *s++;
464	}
465}
466
467
468void
469addchar(c, text)
470	register struct text *text;
471	{
472	struct block *bp;
473
474	if (--text->nleft < 0) {
475		bp = ckmalloc(sizeof *bp);
476		if (text->start == NULL)
477			text->start = bp;
478		else
479			text->last->next = bp;
480		text->last = bp;
481		text->nextc = bp->text;
482		text->nleft = BLOCKSIZE - 1;
483	}
484	*text->nextc++ = c;
485}
486
487/*
488 * Write the contents of a text structure to a file.
489 */
490void
491writetext(text, fp)
492	struct text *text;
493	FILE *fp;
494	{
495	struct block *bp;
496
497	if (text->start != NULL) {
498		for (bp = text->start ; bp != text->last ; bp = bp->next)
499			fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
500		fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
501	}
502}
503
504FILE *
505ckfopen(file, mode)
506	char *file;
507	char *mode;
508	{
509	FILE *fp;
510
511	if ((fp = fopen(file, mode)) == NULL) {
512		fprintf(stderr, "Can't open %s\n", file);
513		exit(2);
514	}
515	return fp;
516}
517
518void *
519ckmalloc(nbytes) {
520	register char *p;
521	char *malloc();
522
523	if ((p = malloc(nbytes)) == NULL)
524		error("Out of space");
525	return p;
526}
527
528char *
529savestr(s)
530	char *s;
531	{
532	register char *p;
533
534	p = ckmalloc(strlen(s) + 1);
535	strcpy(p, s);
536	return p;
537}
538
539void
540error(msg)
541	char *msg;
542	{
543	if (curfile != NULL)
544		fprintf(stderr, "%s:%d: ", curfile, linno);
545	fprintf(stderr, "%s\n", msg);
546	exit(2);
547}
548