| /*- | |
| * Copyright (c) 1991, 1993 | |
| * The Regents of the University of California. All rights reserved. | |
| * Copyright (c) 1997-2005 | |
| * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. | |
| * | |
| * This code is derived from software contributed to Berkeley by | |
| * Kenneth Almquist. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * 3. Neither the name of the University nor the names of its contributors | |
| * may be used to endorse or promote products derived from this software | |
| * without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| * SUCH DAMAGE. | |
| */ | |
| /* | |
| * This program scans all the source files for code to handle various | |
| * special events and combines this code into one file. This (allegedly) | |
| * improves the structure of the program since there is no need for | |
| * anyone outside of a module to know that that module performs special | |
| * operations on particular events. | |
| * | |
| * Usage: mkinit sourcefile... | |
| */ | |
| #include <sys/types.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| /* | |
| * OUTFILE is the name of the output file. Output is initially written | |
| * to the file OUTTEMP, which is then moved to OUTFILE. | |
| */ | |
| #define OUTFILE "init.c" | |
| #define OUTTEMP "init.c.new" | |
| /* | |
| * A text structure is basicly just a string that grows as more characters | |
| * are added onto the end of it. It is implemented as a linked list of | |
| * blocks of characters. The routines addstr and addchar append a string | |
| * or a single character, respectively, to a text structure. Writetext | |
| * writes the contents of a text structure to a file. | |
| */ | |
| #define BLOCKSIZE 512 | |
| struct text { | |
| char *nextc; | |
| int nleft; | |
| struct block *start; | |
| struct block *last; | |
| }; | |
| struct block { | |
| struct block *next; | |
| char text[BLOCKSIZE]; | |
| }; | |
| /* | |
| * There is one event structure for each event that mkinit handles. | |
| */ | |
| struct event { | |
| char *name; /* name of event (e.g. INIT) */ | |
| char *routine; /* name of routine called on event */ | |
| char *comment; /* comment describing routine */ | |
| char *args; /* arguments to routine */ | |
| struct text code; /* code for handling event */ | |
| }; | |
| char writer[] = "\ | |
| /*\n\ | |
| * This file was generated by the mkinit program.\n\ | |
| */\n\ | |
| \n"; | |
| char init[] = "\ | |
| /*\n\ | |
| * Initialization code.\n\ | |
| */\n"; | |
| char exitreset[] = "\ | |
| /*\n\ | |
| * This routine is called when an error or an interrupt occurs in an\n\ | |
| * interactive shell and control is returned to the main command loop\n\ | |
| * but prior to exitshell. \n\ | |
| */\n"; | |
| char forkreset[] = "\ | |
| /*\n\ | |
| * This routine is called when we enter a subshell.\n\ | |
| */\n"; | |
| char postexitreset[] = "\ | |
| /*\n\ | |
| * This routine is called in exitshell.\n\ | |
| */\n"; | |
| char reset[] = "\ | |
| /*\n\ | |
| * This routine is called when an error or an interrupt occurs in an\n\ | |
| * interactive shell and control is returned to the main command loop.\n\ | |
| */\n"; | |
| struct event event[] = { | |
| {"INIT", "init", init}, | |
| {"EXITRESET", "exitreset", exitreset}, | |
| {"FORKRESET", "forkreset", forkreset, "union node *n"}, | |
| {"POSTEXITRESET", "postexitreset", postexitreset}, | |
| {"RESET", "reset", reset}, | |
| {NULL, NULL} | |
| }; | |
| char *curfile; /* current file */ | |
| int linno; /* current line */ | |
| char *header_files[200]; /* list of header files */ | |
| struct text defines; /* #define statements */ | |
| struct text decls; /* declarations */ | |
| int amiddecls; /* for formatting */ | |
| void readfile(char *); | |
| int match(char *, char *); | |
| int gooddefine(char *); | |
| void doevent(struct event *, FILE *, char *); | |
| void doinclude(char *); | |
| void dodecl(char *, FILE *); | |
| void output(void); | |
| void addstr(char *, struct text *); | |
| void addchar(int, struct text *); | |
| void writetext(struct text *, FILE *); | |
| FILE *ckfopen(char *, char *); | |
| void *ckmalloc(int); | |
| char *savestr(char *); | |
| static void error(char *); | |
| int main(int, char **); | |
| #define equal(s1, s2) (strcmp(s1, s2) == 0) | |
| int | |
| main(int argc, char **argv) | |
| { | |
| char **ap; | |
| header_files[0] = "\"shell.h\""; | |
| header_files[1] = "\"mystring.h\""; | |
| header_files[2] = "\"init.h\""; | |
| for (ap = argv + 1 ; *ap ; ap++) | |
| readfile(*ap); | |
| output(); | |
| rename(OUTTEMP, OUTFILE); | |
| exit(0); | |
| /* NOTREACHED */ | |
| } | |
| /* | |
| * Parse an input file. | |
| */ | |
| void | |
| readfile(char *fname) | |
| { | |
| FILE *fp; | |
| char line[1024]; | |
| struct event *ep; | |
| fp = ckfopen(fname, "r"); | |
| curfile = fname; | |
| linno = 0; | |
| amiddecls = 0; | |
| while (fgets(line, sizeof line, fp) != NULL) { | |
| linno++; | |
| for (ep = event ; ep->name ; ep++) { | |
| if (line[0] == ep->name[0] && match(ep->name, line)) { | |
| doevent(ep, fp, fname); | |
| break; | |
| } | |
| } | |
| if (line[0] == 'I' && match("INCLUDE", line)) | |
| doinclude(line); | |
| if (line[0] == 'M' && match("MKINIT", line)) | |
| dodecl(line, fp); | |
| if (line[0] == '#' && gooddefine(line)) { | |
| char *cp; | |
| char line2[1024]; | |
| static const char undef[] = "#undef "; | |
| strcpy(line2, line); | |
| memcpy(line2, undef, sizeof(undef) - 1); | |
| cp = line2 + sizeof(undef) - 1; | |
| while(*cp && (*cp == ' ' || *cp == '\t')) | |
| cp++; | |
| while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n') | |
| cp++; | |
| *cp++ = '\n'; *cp = '\0'; | |
| addstr(line2, &defines); | |
| addstr(line, &defines); | |
| } | |
| } | |
| fclose(fp); | |
| } | |
| int | |
| match(char *name, char *line) | |
| { | |
| char *p, *q; | |
| p = name, q = line; | |
| while (*p) { | |
| if (*p++ != *q++) | |
| return 0; | |
| } | |
| if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n') | |
| return 0; | |
| return 1; | |
| } | |
| int | |
| gooddefine(char *line) | |
| { | |
| char *p; | |
| if (! match("#define", line)) | |
| return 0; /* not a define */ | |
| p = line + 7; | |
| while (*p == ' ' || *p == '\t') | |
| p++; | |
| while (*p != ' ' && *p != '\t') { | |
| if (*p == '(') | |
| return 0; /* macro definition */ | |
| p++; | |
| } | |
| while (*p != '\n' && *p != '\0') | |
| p++; | |
| if (p[-1] == '\\') | |
| return 0; /* multi-line definition */ | |
| return 1; | |
| } | |
| void | |
| doevent(struct event *ep, FILE *fp, char *fname) | |
| { | |
| char line[1024]; | |
| int indent; | |
| char *p; | |
| sprintf(line, "\n /* from %s: */\n", fname); | |
| addstr(line, &ep->code); | |
| addstr(" {\n", &ep->code); | |
| for (;;) { | |
| linno++; | |
| if (fgets(line, sizeof line, fp) == NULL) | |
| error("Unexpected EOF"); | |
| if (equal(line, "}\n")) | |
| break; | |
| indent = 6; | |
| for (p = line ; *p == '\t' ; p++) | |
| indent += 8; | |
| for ( ; *p == ' ' ; p++) | |
| indent++; | |
| if (*p == '\n' || *p == '#') | |
| indent = 0; | |
| while (indent >= 8) { | |
| addchar('\t', &ep->code); | |
| indent -= 8; | |
| } | |
| while (indent > 0) { | |
| addchar(' ', &ep->code); | |
| indent--; | |
| } | |
| addstr(p, &ep->code); | |
| } | |
| addstr(" }\n", &ep->code); | |
| } | |
| void | |
| doinclude(char *line) | |
| { | |
| char *p; | |
| char *name; | |
| char **pp; | |
| for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++); | |
| if (*p == '\0') | |
| error("Expecting '\"' or '<'"); | |
| name = p; | |
| while (*p != ' ' && *p != '\t' && *p != '\n') | |
| p++; | |
| if (p[-1] != '"' && p[-1] != '>') | |
| error("Missing terminator"); | |
| *p = '\0'; | |
| /* name now contains the name of the include file */ | |
| for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++); | |
| if (*pp == NULL) | |
| *pp = savestr(name); | |
| } | |
| void | |
| dodecl(char *line1, FILE *fp) | |
| { | |
| char line[1024]; | |
| char *p, *q; | |
| if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */ | |
| addchar('\n', &decls); | |
| do { | |
| linno++; | |
| if (fgets(line, sizeof line, fp) == NULL) | |
| error("Unterminated structure declaration"); | |
| addstr(line, &decls); | |
| } while (line[0] != '}'); | |
| amiddecls = 0; | |
| } else { | |
| if (! amiddecls) | |
| addchar('\n', &decls); | |
| q = NULL; | |
| for (p = line1 + 6 ; *p && strchr("=/\n", *p) == NULL; p++) | |
| continue; | |
| if (*p == '=') { /* eliminate initialization */ | |
| for (q = p ; *q && *q != ';' ; q++); | |
| if (*q == '\0') | |
| q = NULL; | |
| else { | |
| while (p[-1] == ' ') | |
| p--; | |
| *p = '\0'; | |
| } | |
| } | |
| addstr("extern", &decls); | |
| addstr(line1 + 6, &decls); | |
| if (q != NULL) | |
| addstr(q, &decls); | |
| amiddecls = 1; | |
| } | |
| } | |
| /* | |
| * Write the output to the file OUTTEMP. | |
| */ | |
| void | |
| output(void) | |
| { | |
| FILE *fp; | |
| char **pp; | |
| struct event *ep; | |
| fp = ckfopen(OUTTEMP, "w"); | |
| fputs(writer, fp); | |
| for (pp = header_files ; *pp ; pp++) | |
| fprintf(fp, "#include %s\n", *pp); | |
| fputs("\n\n\n", fp); | |
| writetext(&defines, fp); | |
| fputs("\n\n", fp); | |
| writetext(&decls, fp); | |
| for (ep = event ; ep->name ; ep++) { | |
| fputs("\n\n\n", fp); | |
| fputs(ep->comment, fp); | |
| fprintf(fp, "\nvoid\n%s(%s) {\n", ep->routine, ep->args ?: ""); | |
| writetext(&ep->code, fp); | |
| fprintf(fp, "}\n"); | |
| } | |
| fclose(fp); | |
| } | |
| /* | |
| * A text structure is simply a block of text that is kept in memory. | |
| * Addstr appends a string to the text struct, and addchar appends a single | |
| * character. | |
| */ | |
| void | |
| addstr(char *s, struct text *text) | |
| { | |
| while (*s) { | |
| if (--text->nleft < 0) | |
| addchar(*s++, text); | |
| else | |
| *text->nextc++ = *s++; | |
| } | |
| } | |
| void | |
| addchar(int c, struct text *text) | |
| { | |
| struct block *bp; | |
| if (--text->nleft < 0) { | |
| bp = ckmalloc(sizeof *bp); | |
| if (text->start == NULL) | |
| text->start = bp; | |
| else | |
| text->last->next = bp; | |
| text->last = bp; | |
| text->nextc = bp->text; | |
| text->nleft = BLOCKSIZE - 1; | |
| } | |
| *text->nextc++ = c; | |
| } | |
| /* | |
| * Write the contents of a text structure to a file. | |
| */ | |
| void | |
| writetext(struct text *text, FILE *fp) | |
| { | |
| struct block *bp; | |
| if (text->start != NULL) { | |
| for (bp = text->start ; bp != text->last ; bp = bp->next) { | |
| if ((fwrite(bp->text, sizeof (char), BLOCKSIZE, fp)) != BLOCKSIZE) | |
| error("Can't write data\n"); | |
| } | |
| if ((fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp)) != (BLOCKSIZE - text->nleft)) | |
| error("Can't write data\n"); | |
| } | |
| } | |
| FILE * | |
| ckfopen(char *file, char *mode) | |
| { | |
| FILE *fp; | |
| if ((fp = fopen(file, mode)) == NULL) { | |
| fprintf(stderr, "Can't open %s\n", file); | |
| exit(2); | |
| } | |
| return fp; | |
| } | |
| void * | |
| ckmalloc(int nbytes) | |
| { | |
| char *p; | |
| if ((p = malloc(nbytes)) == NULL) | |
| error("Out of space"); | |
| return p; | |
| } | |
| char * | |
| savestr(char *s) | |
| { | |
| char *p; | |
| p = ckmalloc(strlen(s) + 1); | |
| strcpy(p, s); | |
| return p; | |
| } | |
| static void | |
| error(char *msg) | |
| { | |
| if (curfile != NULL) | |
| fprintf(stderr, "%s:%d: ", curfile, linno); | |
| fprintf(stderr, "%s\n", msg); | |
| exit(2); | |
| /* NOTREACHED */ | |
| } |