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