mkinit.c revision 218306
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 218306 2011-02-04 22:47:55Z jilles $");
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	const char *name;	/* name of event (e.g. INIT) */
106	const char *routine;	/* name of routine called on event */
107	const 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
129
130struct event event[] = {
131	{ "INIT", "init", init, { NULL, 0, NULL, NULL } },
132	{ "RESET", "reset", reset, { NULL, 0, NULL, NULL } },
133	{ NULL, NULL, NULL, { NULL, 0, NULL, NULL } }
134};
135
136
137const char *curfile;			/* current file */
138int linno;				/* current line */
139char *header_files[200];		/* list of header files */
140struct text defines;			/* #define statements */
141struct text decls;			/* declarations */
142int amiddecls;				/* for formatting */
143
144
145void readfile(const char *);
146int match(const char *, const char *);
147int gooddefine(const char *);
148void doevent(struct event *, FILE *, const char *);
149void doinclude(char *);
150void dodecl(char *, FILE *);
151void output(void);
152void addstr(const char *, struct text *);
153void addchar(int, struct text *);
154void writetext(struct text *, FILE *);
155FILE *ckfopen(const char *, const char *);
156void *ckmalloc(size_t);
157char *savestr(const char *);
158void error(const char *);
159
160#define equal(s1, s2)	(strcmp(s1, s2) == 0)
161
162int
163main(int argc __unused, char *argv[])
164{
165	char **ap;
166
167	header_files[0] = savestr("\"shell.h\"");
168	header_files[1] = savestr("\"mystring.h\"");
169	header_files[2] = savestr("\"init.h\"");
170	for (ap = argv + 1 ; *ap ; ap++)
171		readfile(*ap);
172	output();
173	rename(OUTTEMP, OUTFILE);
174	exit(0);
175}
176
177
178/*
179 * Parse an input file.
180 */
181
182void
183readfile(const char *fname)
184{
185	FILE *fp;
186	char line[1024];
187	struct event *ep;
188
189	fp = ckfopen(fname, "r");
190	curfile = fname;
191	linno = 0;
192	amiddecls = 0;
193	while (fgets(line, sizeof line, fp) != NULL) {
194		linno++;
195		for (ep = event ; ep->name ; ep++) {
196			if (line[0] == ep->name[0] && match(ep->name, line)) {
197				doevent(ep, fp, fname);
198				break;
199			}
200		}
201		if (line[0] == 'I' && match("INCLUDE", line))
202			doinclude(line);
203		if (line[0] == 'M' && match("MKINIT", line))
204			dodecl(line, fp);
205		if (line[0] == '#' && gooddefine(line)) {
206			char *cp;
207			char line2[1024];
208			static const char undef[] = "#undef ";
209
210			strcpy(line2, line);
211			memcpy(line2, undef, sizeof(undef) - 1);
212			cp = line2 + sizeof(undef) - 1;
213			while(*cp && (*cp == ' ' || *cp == '\t'))
214			        cp++;
215			while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
216			        cp++;
217			*cp++ = '\n'; *cp = '\0';
218			addstr(line2, &defines);
219			addstr(line, &defines);
220		}
221	}
222	fclose(fp);
223}
224
225
226int
227match(const char *name, const char *line)
228{
229	const char *p, *q;
230
231	p = name, q = line;
232	while (*p) {
233		if (*p++ != *q++)
234			return 0;
235	}
236	if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n')
237		return 0;
238	return 1;
239}
240
241
242int
243gooddefine(const char *line)
244{
245	const char *p;
246
247	if (! match("#define", line))
248		return 0;			/* not a define */
249	p = line + 7;
250	while (*p == ' ' || *p == '\t')
251		p++;
252	while (*p != ' ' && *p != '\t') {
253		if (*p == '(')
254			return 0;		/* macro definition */
255		p++;
256	}
257	while (*p != '\n' && *p != '\0')
258		p++;
259	if (p[-1] == '\\')
260		return 0;			/* multi-line definition */
261	return 1;
262}
263
264
265void
266doevent(struct event *ep, FILE *fp, const char *fname)
267{
268	char line[1024];
269	int indent;
270	const char *p;
271
272	sprintf(line, "\n      /* from %s: */\n", fname);
273	addstr(line, &ep->code);
274	addstr("      {\n", &ep->code);
275	for (;;) {
276		linno++;
277		if (fgets(line, sizeof line, fp) == NULL)
278			error("Unexpected EOF");
279		if (equal(line, "}\n"))
280			break;
281		indent = 6;
282		for (p = line ; *p == '\t' ; p++)
283			indent += 8;
284		for ( ; *p == ' ' ; p++)
285			indent++;
286		if (*p == '\n' || *p == '#')
287			indent = 0;
288		while (indent >= 8) {
289			addchar('\t', &ep->code);
290			indent -= 8;
291		}
292		while (indent > 0) {
293			addchar(' ', &ep->code);
294			indent--;
295		}
296		addstr(p, &ep->code);
297	}
298	addstr("      }\n", &ep->code);
299}
300
301
302void
303doinclude(char *line)
304{
305	char *p;
306	char *name;
307	char **pp;
308
309	for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++);
310	if (*p == '\0')
311		error("Expecting '\"' or '<'");
312	name = p;
313	while (*p != ' ' && *p != '\t' && *p != '\n')
314		p++;
315	if (p[-1] != '"' && p[-1] != '>')
316		error("Missing terminator");
317	*p = '\0';
318
319	/* name now contains the name of the include file */
320	for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++);
321	if (*pp == NULL)
322		*pp = savestr(name);
323}
324
325
326void
327dodecl(char *line1, FILE *fp)
328{
329	char line[1024];
330	char *p, *q;
331
332	if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */
333		addchar('\n', &decls);
334		do {
335			linno++;
336			if (fgets(line, sizeof line, fp) == NULL)
337				error("Unterminated structure declaration");
338			addstr(line, &decls);
339		} while (line[0] != '}');
340		amiddecls = 0;
341	} else {
342		if (! amiddecls)
343			addchar('\n', &decls);
344		q = NULL;
345		for (p = line1 + 6 ; *p && strchr("=/\n", *p) == NULL; p++)
346			continue;
347		if (*p == '=') {		/* eliminate initialization */
348			for (q = p ; *q && *q != ';' ; q++);
349			if (*q == '\0')
350				q = NULL;
351			else {
352				while (p[-1] == ' ')
353					p--;
354				*p = '\0';
355			}
356		}
357		addstr("extern", &decls);
358		addstr(line1 + 6, &decls);
359		if (q != NULL)
360			addstr(q, &decls);
361		amiddecls = 1;
362	}
363}
364
365
366
367/*
368 * Write the output to the file OUTTEMP.
369 */
370
371void
372output(void)
373{
374	FILE *fp;
375	char **pp;
376	struct event *ep;
377
378	fp = ckfopen(OUTTEMP, "w");
379	fputs(writer, fp);
380	for (pp = header_files ; *pp ; pp++)
381		fprintf(fp, "#include %s\n", *pp);
382	fputs("\n\n\n", fp);
383	writetext(&defines, fp);
384	fputs("\n\n", fp);
385	writetext(&decls, fp);
386	for (ep = event ; ep->name ; ep++) {
387		fputs("\n\n\n", fp);
388		fputs(ep->comment, fp);
389		fprintf(fp, "\nvoid\n%s(void)\n{\n", ep->routine);
390		writetext(&ep->code, fp);
391		fprintf(fp, "}\n");
392	}
393	fclose(fp);
394}
395
396
397/*
398 * A text structure is simply a block of text that is kept in memory.
399 * Addstr appends a string to the text struct, and addchar appends a single
400 * character.
401 */
402
403void
404addstr(const char *s, struct text *text)
405{
406	while (*s) {
407		if (--text->nleft < 0)
408			addchar(*s++, text);
409		else
410			*text->nextc++ = *s++;
411	}
412}
413
414
415void
416addchar(int c, struct text *text)
417{
418	struct block *bp;
419
420	if (--text->nleft < 0) {
421		bp = ckmalloc(sizeof *bp);
422		if (text->start == NULL)
423			text->start = bp;
424		else
425			text->last->next = bp;
426		text->last = bp;
427		text->nextc = bp->text;
428		text->nleft = BLOCKSIZE - 1;
429	}
430	*text->nextc++ = c;
431}
432
433/*
434 * Write the contents of a text structure to a file.
435 */
436void
437writetext(struct text *text, FILE *fp)
438{
439	struct block *bp;
440
441	if (text->start != NULL) {
442		for (bp = text->start ; bp != text->last ; bp = bp->next)
443			fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
444		fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
445	}
446}
447
448FILE *
449ckfopen(const char *file, const char *mode)
450{
451	FILE *fp;
452
453	if ((fp = fopen(file, mode)) == NULL) {
454		fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno));
455		exit(2);
456	}
457	return fp;
458}
459
460void *
461ckmalloc(size_t nbytes)
462{
463	char *p;
464
465	if ((p = malloc(nbytes)) == NULL)
466		error("Out of space");
467	return p;
468}
469
470char *
471savestr(const char *s)
472{
473	char *p;
474
475	p = ckmalloc(strlen(s) + 1);
476	strcpy(p, s);
477	return p;
478}
479
480void
481error(const char *msg)
482{
483	if (curfile != NULL)
484		fprintf(stderr, "%s:%d: ", curfile, linno);
485	fprintf(stderr, "%s\n", msg);
486	exit(2);
487}
488