xref: /386bsd/usr/src/bin/sh/mkinit.c (revision a2142627)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * 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  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 char copyright[] =
39 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
40  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #ifndef lint
44 static char sccsid[] = "@(#)mkinit.c	5.3 (Berkeley) 3/13/91";
45 #endif /* not lint */
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.  The command is executed iff init.c
53  * is actually changed.
54  *
55  * Usage:  mkinit command sourcefile...
56  */
57 
58 
59 #include <sys/cdefs.h>
60 #include <stdio.h>
61 #include <fcntl.h>
62 
63 
64 /*
65  * OUTFILE is the name of the output file.  Output is initially written
66  * to the file OUTTEMP, which is then moved to OUTFILE if OUTTEMP and
67  * OUTFILE are different.
68  */
69 
70 #define OUTFILE "init.c"
71 #define OUTTEMP "init.c.new"
72 #define OUTOBJ "init.o"
73 
74 
75 /*
76  * A text structure is basicly just a string that grows as more characters
77  * are added onto the end of it.  It is implemented as a linked list of
78  * blocks of characters.  The routines addstr and addchar append a string
79  * or a single character, respectively, to a text structure.  Writetext
80  * writes the contents of a text structure to a file.
81  */
82 
83 #define BLOCKSIZE 512
84 
85 struct text {
86 	char *nextc;
87 	int nleft;
88 	struct block *start;
89 	struct block *last;
90 };
91 
92 struct block {
93 	struct block *next;
94 	char text[BLOCKSIZE];
95 };
96 
97 
98 /*
99  * There is one event structure for each event that mkinit handles.
100  */
101 
102 struct event {
103 	char *name;		/* name of event (e.g. INIT) */
104 	char *routine;		/* name of routine called on event */
105 	char *comment;		/* comment describing routine */
106 	struct text code;		/* code for handling event */
107 };
108 
109 
110 char writer[] = "\
111 /*\n\
112  * This file was generated by the mkinit program.\n\
113  */\n\
114 \n";
115 
116 char init[] = "\
117 /*\n\
118  * Initialization code.\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 char shellproc[] = "\
128 /*\n\
129  * This routine is called to initialize the shell to run a shell procedure.\n\
130  */\n";
131 
132 
133 struct event event[] = {
134 	{"INIT", "init", init},
135 	{"RESET", "reset", reset},
136 	{"SHELLPROC", "initshellproc", shellproc},
137 	{NULL, NULL}
138 };
139 
140 
141 char *curfile;				/* current file */
142 int linno;				/* current line */
143 char *header_files[200];		/* list of header files */
144 struct text defines;			/* #define statements */
145 struct text decls;			/* declarations */
146 int amiddecls;				/* for formatting */
147 
148 
149 void readfile(), doevent(), doinclude(), dodecl(), output();
150 void addstr(), addchar(), writetext();
151 
152 #define equal(s1, s2)	(strcmp(s1, s2) == 0)
153 
154 FILE *ckfopen();
155 char *savestr();
156 void *ckmalloc __P((int));
157 void error();
158 
main(argc,argv)159 main(argc, argv)
160 	char **argv;
161 	{
162 	char **ap;
163 	int fd;
164 	char c;
165 
166 	if (argc < 2)
167 		error("Usage:  mkinit command file...");
168 	header_files[0] = "\"shell.h\"";
169 	header_files[1] = "\"mystring.h\"";
170 	for (ap = argv + 2 ; *ap ; ap++)
171 		readfile(*ap);
172 	output();
173 	if (file_changed()) {
174 		unlink(OUTFILE);
175 		link(OUTTEMP, OUTFILE);
176 		unlink(OUTTEMP);
177 	} else {
178 		unlink(OUTTEMP);
179 		if (touch(OUTOBJ))
180 			exit(0);		/* no compilation necessary */
181 	}
182 	printf("%s\n", argv[1]);
183 	execl("/bin/sh", "sh", "-c", argv[1], (char *)0);
184 	error("Can't exec shell");
185 }
186 
187 
188 /*
189  * Parse an input file.
190  */
191 
192 void
readfile(fname)193 readfile(fname)
194 	char *fname;
195 	{
196 	FILE *fp;
197 	char line[1024];
198 	struct event *ep;
199 
200 	fp = ckfopen(fname, "r");
201 	curfile = fname;
202 	linno = 0;
203 	amiddecls = 0;
204 	while (fgets(line, sizeof line, fp) != NULL) {
205 		linno++;
206 		for (ep = event ; ep->name ; ep++) {
207 			if (line[0] == ep->name[0] && match(ep->name, line)) {
208 				doevent(ep, fp, fname);
209 				break;
210 			}
211 		}
212 		if (line[0] == 'I' && match("INCLUDE", line))
213 			doinclude(line);
214 		if (line[0] == 'M' && match("MKINIT", line))
215 			dodecl(line, fp);
216 		if (line[0] == '#' && gooddefine(line))
217 			addstr(line, &defines);
218 	}
219 	fclose(fp);
220 }
221 
222 
223 int
match(name,line)224 match(name, line)
225 	char *name;
226 	char *line;
227 	{
228 	register char *p, *q;
229 
230 	p = name, q = line;
231 	while (*p) {
232 		if (*p++ != *q++)
233 			return 0;
234 	}
235 	if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n')
236 		return 0;
237 	return 1;
238 }
239 
240 
241 int
gooddefine(line)242 gooddefine(line)
243 	char *line;
244 	{
245 	register 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 
265 void
doevent(ep,fp,fname)266 doevent(ep, fp, fname)
267 	register struct event *ep;
268 	FILE *fp;
269 	char *fname;
270 	{
271 	char line[1024];
272 	int indent;
273 	char *p;
274 
275 	sprintf(line, "\n      /* from %s: */\n", fname);
276 	addstr(line, &ep->code);
277 	addstr("      {\n", &ep->code);
278 	for (;;) {
279 		linno++;
280 		if (fgets(line, sizeof line, fp) == NULL)
281 			error("Unexpected EOF");
282 		if (equal(line, "}\n"))
283 			break;
284 		indent = 6;
285 		for (p = line ; *p == '\t' ; p++)
286 			indent += 8;
287 		for ( ; *p == ' ' ; p++)
288 			indent++;
289 		if (*p == '\n' || *p == '#')
290 			indent = 0;
291 		while (indent >= 8) {
292 			addchar('\t', &ep->code);
293 			indent -= 8;
294 		}
295 		while (indent > 0) {
296 			addchar(' ', &ep->code);
297 			indent--;
298 		}
299 		addstr(p, &ep->code);
300 	}
301 	addstr("      }\n", &ep->code);
302 }
303 
304 
305 void
doinclude(line)306 doinclude(line)
307 	char *line;
308 	{
309 	register char *p;
310 	char *name;
311 	register char **pp;
312 
313 	for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++);
314 	if (*p == '\0')
315 		error("Expecting '\"' or '<'");
316 	name = p;
317 	while (*p != ' ' && *p != '\t' && *p != '\n')
318 		p++;
319 	if (p[-1] != '"' && p[-1] != '>')
320 		error("Missing terminator");
321 	*p = '\0';
322 
323 	/* name now contains the name of the include file */
324 	for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++);
325 	if (*pp == NULL)
326 		*pp = savestr(name);
327 }
328 
329 
330 void
dodecl(line1,fp)331 dodecl(line1, fp)
332 	char *line1;
333 	FILE *fp;
334 	{
335 	char line[1024];
336 	register 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 != '=' && *p != '/' ; p++);
352 		if (*p == '=') {		/* eliminate initialization */
353 			for (q = p ; *q && *q != ';' ; q++);
354 			if (*q == '\0')
355 				q = NULL;
356 			else {
357 				while (p[-1] == ' ')
358 					p--;
359 				*p = '\0';
360 			}
361 		}
362 		addstr("extern", &decls);
363 		addstr(line1 + 6, &decls);
364 		if (q != NULL)
365 			addstr(q, &decls);
366 		amiddecls = 1;
367 	}
368 }
369 
370 
371 
372 /*
373  * Write the output to the file OUTTEMP.
374  */
375 
376 void
output()377 output() {
378 	FILE *fp;
379 	char **pp;
380 	struct event *ep;
381 
382 	fp = ckfopen(OUTTEMP, "w");
383 	fputs(writer, fp);
384 	for (pp = header_files ; *pp ; pp++)
385 		fprintf(fp, "#include %s\n", *pp);
386 	fputs("\n\n\n", fp);
387 	writetext(&defines, fp);
388 	fputs("\n\n", fp);
389 	writetext(&decls, fp);
390 	for (ep = event ; ep->name ; ep++) {
391 		fputs("\n\n\n", fp);
392 		fputs(ep->comment, fp);
393 		fprintf(fp, "\nvoid\n%s() {\n", ep->routine);
394 		writetext(&ep->code, fp);
395 		fprintf(fp, "}\n");
396 	}
397 	fclose(fp);
398 }
399 
400 
401 /*
402  * Return true if the new output file is different from the old one.
403  */
404 
405 int
file_changed()406 file_changed() {
407 	register FILE *f1, *f2;
408 	register int c;
409 
410 	if ((f1 = fopen(OUTFILE, "r")) == NULL
411 	 || (f2 = fopen(OUTTEMP, "r")) == NULL)
412 		return 1;
413 	while ((c = getc(f1)) == getc(f2)) {
414 		if (c == EOF)
415 			return 0;
416 	}
417 	return 1;
418 }
419 
420 
421 /*
422  * Touch a file.  Returns 0 on failure, 1 on success.
423  */
424 
425 int
touch(file)426 touch(file)
427 	char *file;
428 	{
429 	int fd;
430 	char c;
431 
432 	if ((fd = open(file, O_RDWR)) < 0)
433 		return 0;
434 	if (read(fd, &c, 1) != 1) {
435 		close(fd);
436 		return 0;
437 	}
438 	lseek(fd, 0L, 0);
439 	write(fd, &c, 1);
440 	close(fd);
441 	return 1;
442 }
443 
444 
445 
446 /*
447  * A text structure is simply a block of text that is kept in memory.
448  * Addstr appends a string to the text struct, and addchar appends a single
449  * character.
450  */
451 
452 void
addstr(s,text)453 addstr(s, text)
454 	register char *s;
455 	register struct text *text;
456 	{
457 	while (*s) {
458 		if (--text->nleft < 0)
459 			addchar(*s++, text);
460 		else
461 			*text->nextc++ = *s++;
462 	}
463 }
464 
465 
466 void
addchar(c,text)467 addchar(c, text)
468 	register struct text *text;
469 	{
470 	struct block *bp;
471 
472 	if (--text->nleft < 0) {
473 		bp = ckmalloc(sizeof *bp);
474 		if (text->start == NULL)
475 			text->start = bp;
476 		else
477 			text->last->next = bp;
478 		text->last = bp;
479 		text->nextc = bp->text;
480 		text->nleft = BLOCKSIZE - 1;
481 	}
482 	*text->nextc++ = c;
483 }
484 
485 /*
486  * Write the contents of a text structure to a file.
487  */
488 void
writetext(text,fp)489 writetext(text, fp)
490 	struct text *text;
491 	FILE *fp;
492 	{
493 	struct block *bp;
494 
495 	if (text->start != NULL) {
496 		for (bp = text->start ; bp != text->last ; bp = bp->next)
497 			fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
498 		fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
499 	}
500 }
501 
502 FILE *
ckfopen(file,mode)503 ckfopen(file, mode)
504 	char *file;
505 	char *mode;
506 	{
507 	FILE *fp;
508 
509 	if ((fp = fopen(file, mode)) == NULL) {
510 		fprintf(stderr, "Can't open %s\n", file);
511 		exit(2);
512 	}
513 	return fp;
514 }
515 
516 void *
ckmalloc(nbytes)517 ckmalloc(nbytes) {
518 	register char *p;
519 	char *malloc();
520 
521 	if ((p = malloc(nbytes)) == NULL)
522 		error("Out of space");
523 	return p;
524 }
525 
526 char *
savestr(s)527 savestr(s)
528 	char *s;
529 	{
530 	register char *p;
531 
532 	p = ckmalloc(strlen(s) + 1);
533 	strcpy(p, s);
534 	return p;
535 }
536 
537 void
error(msg)538 error(msg)
539 	char *msg;
540 	{
541 	if (curfile != NULL)
542 		fprintf(stderr, "%s:%d: ", curfile, linno);
543 	fprintf(stderr, "%s\n", msg);
544 	exit(2);
545 }
546