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