Commit | Line | Data |
---|---|---|
15637ed4 RG |
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 | ||
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 * | |
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 * | |
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 * | |
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 | |
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 | } |