aa9caaf657
merge of parallel duplicate work by Steve Price and myself. :-] There are some changes to the build that are my fault... mkinit.c was trying (poorly) to duplicate some of the work that make(1) is designed to do. The Makefile hackery is my fault too, the depend list was incomplete because of some explicit OBJS+= entries, so mkdep wasn't picking up their source file #includes. This closes a pile of /bin/sh PR's, but not all of them.. Submitted by: Steve Price <steve@bonsai.hiwaay.net>, peter
518 lines
11 KiB
C
518 lines
11 KiB
C
/*-
|
|
* Copyright (c) 1991, 1993
|
|
* The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. 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.
|
|
*
|
|
* $Id: mkinit.c,v 1.5 1995/10/01 15:13:31 joerg Exp $
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char copyright[] =
|
|
"@(#) Copyright (c) 1991, 1993\n\
|
|
The Regents of the University of California. All rights reserved.\n";
|
|
#endif /* not lint */
|
|
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)mkinit.c 8.2 (Berkeley) 5/4/95";
|
|
#endif /* not lint */
|
|
|
|
/*
|
|
* 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/cdefs.h>
|
|
#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 if OUTTEMP and
|
|
* OUTFILE are different.
|
|
*/
|
|
|
|
#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 */
|
|
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 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";
|
|
|
|
char shellproc[] = "\
|
|
/*\n\
|
|
* This routine is called to initialize the shell to run a shell procedure.\n\
|
|
*/\n";
|
|
|
|
|
|
struct event event[] = {
|
|
{"INIT", "init", init},
|
|
{"RESET", "reset", reset},
|
|
{"SHELLPROC", "initshellproc", shellproc},
|
|
{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 __P((char *));
|
|
int match __P((char *, char *));
|
|
int gooddefine __P((char *));
|
|
void doevent __P((struct event *, FILE *, char *));
|
|
void doinclude __P((char *));
|
|
void dodecl __P((char *, FILE *));
|
|
void output __P((void));
|
|
void addstr __P((char *, struct text *));
|
|
void addchar __P((int, struct text *));
|
|
void writetext __P((struct text *, FILE *));
|
|
FILE *ckfopen __P((char *, char *));
|
|
void *ckmalloc __P((int));
|
|
char *savestr __P((char *));
|
|
void error __P((char *));
|
|
|
|
#define equal(s1, s2) (strcmp(s1, s2) == 0)
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
char **ap;
|
|
|
|
if (argc < 2)
|
|
error("Usage: mkinit file...");
|
|
header_files[0] = "\"shell.h\"";
|
|
header_files[1] = "\"mystring.h\"";
|
|
for (ap = argv + 1 ; *ap ; ap++)
|
|
readfile(*ap);
|
|
output();
|
|
unlink(OUTFILE);
|
|
link(OUTTEMP, OUTFILE);
|
|
unlink(OUTTEMP);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse an input file.
|
|
*/
|
|
|
|
void
|
|
readfile(fname)
|
|
char *fname;
|
|
{
|
|
FILE *fp;
|
|
char line[1024], line2[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;
|
|
|
|
strcpy(line2, line);
|
|
memcpy(line2, "#undef ", strlen("#undef "));
|
|
cp = line2 + strlen("#undef ");
|
|
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(name, line)
|
|
char *name;
|
|
char *line;
|
|
{
|
|
register 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(line)
|
|
char *line;
|
|
{
|
|
register 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(ep, fp, fname)
|
|
register 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(line)
|
|
char *line;
|
|
{
|
|
register char *p;
|
|
char *name;
|
|
register 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(line1, fp)
|
|
char *line1;
|
|
FILE *fp;
|
|
{
|
|
char line[1024];
|
|
register 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 != '\0' && *p != '=' && *p != '/' && *p != '\n'; p++);
|
|
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() {
|
|
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() {\n", ep->routine);
|
|
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(s, text)
|
|
register char *s;
|
|
register struct text *text;
|
|
{
|
|
while (*s) {
|
|
if (--text->nleft < 0)
|
|
addchar(*s++, text);
|
|
else
|
|
*text->nextc++ = *s++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
addchar(c, text)
|
|
int c;
|
|
register 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(text, fp)
|
|
struct text *text;
|
|
FILE *fp;
|
|
{
|
|
struct block *bp;
|
|
|
|
if (text->start != NULL) {
|
|
for (bp = text->start ; bp != text->last ; bp = bp->next)
|
|
fwrite(bp->text, sizeof (char), BLOCKSIZE, fp);
|
|
fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp);
|
|
}
|
|
}
|
|
|
|
FILE *
|
|
ckfopen(file, mode)
|
|
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(nbytes)
|
|
int nbytes;
|
|
{
|
|
register char *p;
|
|
|
|
if ((p = malloc(nbytes)) == NULL)
|
|
error("Out of space");
|
|
return p;
|
|
}
|
|
|
|
char *
|
|
savestr(s)
|
|
char *s;
|
|
{
|
|
register char *p;
|
|
|
|
p = ckmalloc(strlen(s) + 1);
|
|
strcpy(p, s);
|
|
return p;
|
|
}
|
|
|
|
void
|
|
error(msg)
|
|
char *msg;
|
|
{
|
|
if (curfile != NULL)
|
|
fprintf(stderr, "%s:%d: ", curfile, linno);
|
|
fprintf(stderr, "%s\n", msg);
|
|
exit(2);
|
|
}
|