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
main(argc,argv)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
readfile(fname)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
match(name,line)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
gooddefine(line)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
doevent(ep,fp,fname)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
doinclude(line)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
dodecl(line1,fp)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
output()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
file_changed()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
touch(file)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
addstr(s,text)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
addchar(c,text)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
writetext(text,fp)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 *
ckfopen(file,mode)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 *
ckmalloc(nbytes)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 *
savestr(s)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
error(msg)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