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