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