mkinit.c revision 149019
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 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34static char const copyright[] =
35"@(#) Copyright (c) 1991, 1993\n\
36	The Regents of the University of California.  All rights reserved.\n";
37#endif /* not lint */
38
39#ifndef lint
40#if 0
41static char sccsid[] = "@(#)mkinit.c	8.2 (Berkeley) 5/4/95";
42#endif
43#endif /* not lint */
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: head/bin/sh/mkinit.c 149019 2005-08-13 08:38:02Z stefanf $");
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.
53 *
54 * Usage:  mkinit sourcefile...
55 */
56
57
58#include <sys/cdefs.h>
59#include <sys/types.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#include <fcntl.h>
64#include <unistd.h>
65#include <errno.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.
71 */
72
73#define OUTFILE "init.c"
74#define OUTTEMP "init.c.new"
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, { NULL, 0, NULL, NULL } },
137	{ "RESET", "reset", reset, { NULL, 0, NULL, NULL } },
138	{ "SHELLPROC", "initshellproc", shellproc, { NULL, 0, NULL, NULL } },
139	{ NULL, NULL, NULL, { NULL, 0, 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(char *);
152int match(char *, char *);
153int gooddefine(char *);
154void doevent(struct event *, FILE *, char *);
155void doinclude(char *);
156void dodecl(char *, FILE *);
157void output(void);
158void addstr(char *, struct text *);
159void addchar(int, struct text *);
160void writetext(struct text *, FILE *);
161FILE *ckfopen(char *, char *);
162void *ckmalloc(int);
163char *savestr(char *);
164void error(char *);
165
166#define equal(s1, s2)	(strcmp(s1, s2) == 0)
167
168int
169main(int argc __unused, char *argv[])
170{
171	char **ap;
172
173	header_files[0] = "\"shell.h\"";
174	header_files[1] = "\"mystring.h\"";
175	header_files[2] = "\"init.h\"";
176	for (ap = argv + 1 ; *ap ; ap++)
177		readfile(*ap);
178	output();
179	rename(OUTTEMP, OUTFILE);
180	exit(0);
181}
182
183
184/*
185 * Parse an input file.
186 */
187
188void
189readfile(char *fname)
190{
191	FILE *fp;
192	char line[1024];
193	struct event *ep;
194
195	fp = ckfopen(fname, "r");
196	curfile = fname;
197	linno = 0;
198	amiddecls = 0;
199	while (fgets(line, sizeof line, fp) != NULL) {
200		linno++;
201		for (ep = event ; ep->name ; ep++) {
202			if (line[0] == ep->name[0] && match(ep->name, line)) {
203				doevent(ep, fp, fname);
204				break;
205			}
206		}
207		if (line[0] == 'I' && match("INCLUDE", line))
208			doinclude(line);
209		if (line[0] == 'M' && match("MKINIT", line))
210			dodecl(line, fp);
211		if (line[0] == '#' && gooddefine(line)) {
212			char *cp;
213			char line2[1024];
214			static const char undef[] = "#undef ";
215
216			strcpy(line2, line);
217			memcpy(line2, undef, sizeof(undef) - 1);
218			cp = line2 + sizeof(undef) - 1;
219			while(*cp && (*cp == ' ' || *cp == '\t'))
220			        cp++;
221			while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
222			        cp++;
223			*cp++ = '\n'; *cp = '\0';
224			addstr(line2, &defines);
225			addstr(line, &defines);
226		}
227	}
228	fclose(fp);
229}
230
231
232int
233match(char *name, char *line)
234{
235	char *p, *q;
236
237	p = name, q = line;
238	while (*p) {
239		if (*p++ != *q++)
240			return 0;
241	}
242	if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n')
243		return 0;
244	return 1;
245}
246
247
248int
249gooddefine(char *line)
250{
251	char *p;
252
253	if (! match("#define", line))
254		return 0;			/* not a define */
255	p = line + 7;
256	while (*p == ' ' || *p == '\t')
257		p++;
258	while (*p != ' ' && *p != '\t') {
259		if (*p == '(')
260			return 0;		/* macro definition */
261		p++;
262	}
263	while (*p != '\n' && *p != '\0')
264		p++;
265	if (p[-1] == '\\')
266		return 0;			/* multi-line definition */
267	return 1;
268}
269
270
271void
272doevent(struct event *ep, FILE *fp, char *fname)
273{
274	char line[1024];
275	int indent;
276	char *p;
277
278	sprintf(line, "\n      /* from %s: */\n", fname);
279	addstr(line, &ep->code);
280	addstr("      {\n", &ep->code);
281	for (;;) {
282		linno++;
283		if (fgets(line, sizeof line, fp) == NULL)
284			error("Unexpected EOF");
285		if (equal(line, "}\n"))
286			break;
287		indent = 6;
288		for (p = line ; *p == '\t' ; p++)
289			indent += 8;
290		for ( ; *p == ' ' ; p++)
291			indent++;
292		if (*p == '\n' || *p == '#')
293			indent = 0;
294		while (indent >= 8) {
295			addchar('\t', &ep->code);
296			indent -= 8;
297		}
298		while (indent > 0) {
299			addchar(' ', &ep->code);
300			indent--;
301		}
302		addstr(p, &ep->code);
303	}
304	addstr("      }\n", &ep->code);
305}
306
307
308void
309doinclude(char *line)
310{
311	char *p;
312	char *name;
313	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(char *line1, FILE *fp)
334{
335	char line[1024];
336	char *p, *q;
337
338	if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */
339		addchar('\n', &decls);
340		do {
341			linno++;
342			if (fgets(line, sizeof line, fp) == NULL)
343				error("Unterminated structure declaration");
344			addstr(line, &decls);
345		} while (line[0] != '}');
346		amiddecls = 0;
347	} else {
348		if (! amiddecls)
349			addchar('\n', &decls);
350		q = NULL;
351		for (p = line1 + 6 ; *p && strchr("=/\n", *p) == NULL; p++)
352			continue;
353		if (*p == '=') {		/* eliminate initialization */
354			for (q = p ; *q && *q != ';' ; q++);
355			if (*q == '\0')
356				q = NULL;
357			else {
358				while (p[-1] == ' ')
359					p--;
360				*p = '\0';
361			}
362		}
363		addstr("extern", &decls);
364		addstr(line1 + 6, &decls);
365		if (q != NULL)
366			addstr(q, &decls);
367		amiddecls = 1;
368	}
369}
370
371
372
373/*
374 * Write the output to the file OUTTEMP.
375 */
376
377void
378output(void)
379{
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(void)\n{\n", ep->routine);
396		writetext(&ep->code, fp);
397		fprintf(fp, "}\n");
398	}
399	fclose(fp);
400}
401
402
403/*
404 * A text structure is simply a block of text that is kept in memory.
405 * Addstr appends a string to the text struct, and addchar appends a single
406 * character.
407 */
408
409void
410addstr(char *s, struct text *text)
411{
412	while (*s) {
413		if (--text->nleft < 0)
414			addchar(*s++, text);
415		else
416			*text->nextc++ = *s++;
417	}
418}
419
420
421void
422addchar(int c, struct text *text)
423{
424	struct block *bp;
425
426	if (--text->nleft < 0) {
427		bp = ckmalloc(sizeof *bp);
428		if (text->start == NULL)
429			text->start = bp;
430		else
431			text->last->next = bp;
432		text->last = bp;
433		text->nextc = bp->text;
434		text->nleft = BLOCKSIZE - 1;
435	}
436	*text->nextc++ = c;
437}
438
439/*
440 * Write the contents of a text structure to a file.
441 */
442void
443writetext(struct text *text, FILE *fp)
444{
445	struct block *bp;
446
447	if (text->start != NULL) {
448		for (bp = text->start ; bp != text->last ; bp = bp->next)
449			fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
450		fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
451	}
452}
453
454FILE *
455ckfopen(char *file, char *mode)
456{
457	FILE *fp;
458
459	if ((fp = fopen(file, mode)) == NULL) {
460		fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno));
461		exit(2);
462	}
463	return fp;
464}
465
466void *
467ckmalloc(int nbytes)
468{
469	char *p;
470
471	if ((p = malloc(nbytes)) == NULL)
472		error("Out of space");
473	return p;
474}
475
476char *
477savestr(char *s)
478{
479	char *p;
480
481	p = ckmalloc(strlen(s) + 1);
482	strcpy(p, s);
483	return p;
484}
485
486void
487error(char *msg)
488{
489	if (curfile != NULL)
490		fprintf(stderr, "%s:%d: ", curfile, linno);
491	fprintf(stderr, "%s\n", msg);
492	exit(2);
493}
494