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