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