minix/commands/proto/proto.c
2010-05-12 16:28:54 +00:00

689 lines
14 KiB
C

/* proto - Generate ANSI C prototypes. Author: Eric R. Smith */
/* Program to extract function declarations from C source code
* Written by Eric R. Smith and placed in the public domain
* Thanks are due to Jwahar R. Bammi for fixing several bugs
* And providing the Unix makefiles.
*/
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_' ))
#define ABORTED ( (Word *) -1 )
#define MAXPARAM 20 /* max. number of parameters to a function */
typedef struct word {
struct word *next;
char string[1];
} Word;
int inquote = 0; /* in a quote? */
int newline_seen = 1; /* are we at the start of a line */
long linenum = 1L; /* line number in current file */
long endline = 0L; /* the last line before the { of a f'n */
long symline = 0L; /* Line that symbol was on, set by getsym() */
int dostatic = 0; /* do static functions? */
int donum = 0; /* print line numbers? */
int dohead = 1; /* do file headers? */
int docond = 1; /* conditionalize for non-ANSI compilers? */
int dodiff = 0; /* Output a diff file to prototype original */
int doold = 0; /* do old style: P() */
int glastc = ' '; /* last char. seen by getsym() */
Word *endlist; /* Parentheses after the parameters */
char *progname; /* name of program (for error messages) */
_PROTOTYPE(Word * word_alloc, (char *s));
_PROTOTYPE(void word_free, (Word * w));
_PROTOTYPE(int List_len, (Word * w));
_PROTOTYPE(Word * word_append, (Word * w1, Word * w2));
_PROTOTYPE(int foundin, (Word * w1, Word * w2));
_PROTOTYPE(void addword, (Word * w, char *s));
_PROTOTYPE(void printlist, (Word * p));
_PROTOTYPE(Word * typelist, (Word * p));
_PROTOTYPE(void typefixhack, (Word * w));
_PROTOTYPE(int ngetc, (FILE * f));
_PROTOTYPE(int fnextch, (FILE * f));
_PROTOTYPE(int nextch, (FILE * f));
_PROTOTYPE(int getsym, (char *buf, FILE * f));
_PROTOTYPE(int skipit, (char *buf, FILE * f));
_PROTOTYPE(Word * getparamlist, (FILE * f));
_PROTOTYPE(void emit, (Word * wlist, Word * plist, long startline));
_PROTOTYPE(void getdecl, (FILE * f));
_PROTOTYPE(int main, (int argc, char **argv));
_PROTOTYPE(void Usage, (void));
/* Routines for manipulating lists of words. */
Word *word_alloc(s)
char *s;
{
Word *w;
w = (Word *) malloc(sizeof(Word) + strlen(s) + 1);
if (w == NULL) {
fprintf(stderr, "%s: out of memory\n", progname);
exit(1);
}
(void) strcpy(w->string, s);
w->next = NULL;
return w;
}
void word_free(w)
Word *w;
{
Word *oldw;
while (w) {
oldw = w;
w = w->next;
free((char *) oldw);
}
}
/* Return the length of a list; empty words are not counted */
int List_len(w)
Word *w;
{
int count = 0;
while (w) {
if (*w->string) count++;
w = w->next;
}
return count;
}
/* Append two lists, and return the result */
Word *word_append(w1, w2)
Word *w1, *w2;
{
Word *r, *w;
r = w = word_alloc("");
while (w1) {
w->next = word_alloc(w1->string);
w = w->next;
w1 = w1->next;
}
while (w2) {
w->next = word_alloc(w2->string);
w = w->next;
w2 = w2->next;
}
return r;
}
/* See if the last entry in w2 is in w1 */
int foundin(w1, w2)
Word *w1, *w2;
{
while (w2->next) w2 = w2->next;
while (w1) {
if (!strcmp(w1->string, w2->string)) return 1;
w1 = w1->next;
}
return 0;
}
/* Add the string s to the given list of words */
void addword(w, s)
Word *w;
char *s;
{
while (w->next) w = w->next;
w->next = word_alloc(s);
}
/* Printlist: print out a list */
void printlist(p)
Word *p;
{
Word *w;
int i = 0;
for (w = p; w; w = w->next) {
printf("%s", w->string);
if (ISCSYM(w->string[0]) && i > 0
&& w->next && w->next->string[0] != ',') printf(" ");
i++;
}
}
/* Given a list representing a type and a variable name, extract just
* the base type, e.g. "struct word *x" would yield "struct word".
* Similarly, "unsigned char x[]" would yield "unsigned char".
*/
Word *typelist(p)
Word *p;
{
Word *w, *r, *last;
last = r = w = word_alloc("");
while (p && p->next) {
if (p->string[0] == '[') {
word_free(w);
last->next = NULL;
break;
}
if (p->string[0] && !ISCSYM(p->string[0])) break;
w->next = word_alloc(p->string);
last = w;
w = w->next;
p = p->next;
}
return r;
}
/* Typefixhack: promote formal parameters of type "char", "unsigned char",
* "short", or "unsigned short" to "int".
*/
void typefixhack(w)
Word *w;
{
Word *oldw = 0;
while (w) {
if (*w->string) {
if ((!strcmp(w->string, "char") ||
!strcmp(w->string, "short"))
&& (List_len(w->next) < 2)) {
if (oldw && !strcmp(oldw->string, "unsigned")) {
oldw->next = w->next;
free((char *) w);
w = oldw;
}
(void) strcpy(w->string, "int");
}
}
w = w->next;
}
}
/* Read a character: if it's a newline, increment the line count */
int ngetc(f)
FILE *f;
{
int c;
c = getc(f);
if (c == '\n') linenum++;
return c;
}
/* Read the next character from the file. If the character is '\' then
* read and skip the next character. Any comment sequence is converted
* to a blank.
*/
int fnextch(f)
FILE *f;
{
int c, lastc, incomment;
c = ngetc(f);
while (c == '\\') {
c = ngetc(f); /* skip a character */
c = ngetc(f);
}
if (c == '/' && !inquote) {
c = ngetc(f);
if (c == '*') {
incomment = 1;
c = ' ';
while (incomment) {
lastc = c;
c = ngetc(f);
if (lastc == '*' && c == '/')
incomment = 0;
else if (c < 0)
return c;
}
return fnextch(f);
} else {
if (c == '\n') linenum--;
(void) ungetc(c, f);
return '/';
}
}
return c;
}
/* Get the next "interesting" character. Comments are skipped, and strings
* are converted to "0". Also, if a line starts with "#" it is skipped.
*/
int nextch(f)
FILE *f;
{
int c;
c = fnextch(f);
if (newline_seen && c == '#') {
do {
c = fnextch(f);
} while (c >= 0 && c != '\n');
if (c < 0) return c;
}
newline_seen = (c == '\n');
if (c == '\'' || c == '\"') {
inquote = c;
while ((c = fnextch(f)) >= 0) {
if (c == inquote) {
inquote = 0;
return '0';
}
}
}
return c;
}
/* Get the next symbol from the file, skipping blanks.
* Return 0 if OK, -1 for EOF.
* Also collapses everything between { and }
*/
int getsym(buf, f)
char *buf;
FILE *f;
{
register int c;
int inbrack = 0;
c = glastc;
while ((c > 0) && isspace(c)) c = nextch(f);
if (c < 0) return -1;
if (c == '{') {
inbrack = 1;
endline = linenum;
while (inbrack) {
c = nextch(f);
if (c < 0) {
glastc = c;
return c;
}
if (c == '{')
inbrack++;
else if (c == '}')
inbrack--;
}
(void) strcpy(buf, "{}");
glastc = nextch(f);
return 0;
}
if (!ISCSYM(c)) {
*buf++ = c;
glastc = nextch(f);
if (c == '(' && glastc == '*') { /* Look for a 'f'n pointer */
*buf++ = glastc;
glastc = nextch(f);
}
*buf = 0;
return 0;
}
symline = linenum;
while (ISCSYM(c)) {
*buf++ = c;
c = nextch(f);
}
*buf = 0;
glastc = c;
return 0;
}
/* Skipit: skip until a ";" or the end of a function declaration is seen */
int skipit(buf, f)
char *buf;
FILE *f;
{
int i;
do {
i = getsym(buf, f);
if (i < 0) return i;
} while (*buf != ';' && *buf != '{');
return 0;
}
/* Get a parameter list; when this is called the next symbol in line
* should be the first thing in the list.
*/
Word *getparamlist(f)
FILE *f;
{
static Word *pname[MAXPARAM]; /* parameter names */
Word *tlist, /* type name */
*plist; /* temporary */
int np = 0; /* number of parameters */
int typed[MAXPARAM]; /* parameter has been given a type */
int tlistdone; /* finished finding the type name */
int sawsomething;
int i;
int inparen = 0;
char buf[80];
for (i = 0; i < MAXPARAM; i++) typed[i] = 0;
plist = word_alloc("");
endlist = word_alloc("");
/* First, get the stuff inside brackets (if anything) */
sawsomething = 0; /* gets set nonzero when we see an arg */
for (;;) {
if (getsym(buf, f) < 0) return(NULL);
if (*buf == ')' && (--inparen < 0)) {
if (sawsomething) { /* if we've seen an arg */
pname[np] = plist;
plist = word_alloc("");
np++;
}
break;
}
if (*buf == ';') { /* something weird */
return ABORTED;
}
sawsomething = 1; /* there's something in the arg. list */
if (*buf == ',' && inparen == 0) {
pname[np] = plist;
plist = word_alloc("");
np++;
} else {
addword(plist, buf);
if (*buf == '(') inparen++;
}
}
/* Next, get the declarations after the function header */
inparen = 0;
tlist = word_alloc("");
plist = word_alloc("");
tlistdone = 0;
sawsomething = 0;
for (;;) {
if (getsym(buf, f) < 0) return(NULL);
/* Handle parentheses, which should indicate func pointer rtn values */
if (*buf == '(') {
addword(endlist, buf);
addword(endlist, " void ");
inparen++;
} else if (*buf == ')') {
if (symline == linenum) {
addword(endlist, buf);
addword(endlist, buf);
}
inparen--;
} else if (*buf == ',' && !inparen) {
/* Handle a list like "int x,y,z" */
if (!sawsomething) return(NULL);
for (i = 0; i < np; i++) {
if (!typed[i] && foundin(plist, pname[i])) {
typed[i] = 1;
word_free(pname[i]);
pname[i] = word_append(tlist, plist);
/* Promote types */
typefixhack(pname[i]);
break;
}
}
if (!tlistdone) {
tlist = typelist(plist);
tlistdone = 1;
}
word_free(plist);
plist = word_alloc("");
} else if (*buf == ';') {
/* Handle the end of a list */
if (!sawsomething) return ABORTED;
for (i = 0; i < np; i++) {
if (!typed[i] && foundin(plist, pname[i])) {
typed[i] = 1;
word_free(pname[i]);
pname[i] = word_append(tlist, plist);
typefixhack(pname[i]);
break;
}
}
tlistdone = 0;
word_free(tlist);
word_free(plist);
tlist = word_alloc("");
plist = word_alloc("");
} else if (!strcmp(buf, "{}"))
break; /* Handle the beginning of the function */
/* Otherwise, throw word into list (except for "register") */
else if (strcmp(buf, "register")) {
sawsomething = 1;
addword(plist, buf);
if (*buf == '(') inparen++;
if (*buf == ')') inparen--;
}
}
/* Now take the info we have and build a prototype list */
/* Empty parameter list means "void" */
if (np == 0) return word_alloc("void");
plist = tlist = word_alloc("");
for (i = 0; i < np; i++) {
/* If no type provided, make it an "int" */
if (!(pname[i]->next) ||
(!(pname[i]->next->next)&&strcmp(pname[i]->next->string,"void"))) {
addword(tlist, "int");
}
while (tlist->next) tlist = tlist->next;
tlist->next = pname[i];
if (i < np - 1) addword(tlist, ", ");
}
return plist;
}
/* Emit a function declaration. The attributes and name of the function
* are in wlist; the parameters are in plist.
*/
void emit(wlist, plist, startline)
Word *wlist, *plist;
long startline;
{
Word *w;
int count = 0;
if (doold == 0) printf("_PROTOTYPE( ");
if (dodiff) {
printf("%lda%ld,%ld\n", startline - 1, startline, startline +2);
printf("> #ifdef __STDC__\n> ");
}
if (donum) printf("/*%8ld */ ", startline);
for (w = wlist; w; w = w->next) {
if (w->string[0]) count++;
}
if (count < 2) printf("int ");
printlist(wlist);
if (docond) {
if (doold)
printf(" P((");
else
printf(", (");
} else {
printf("(");
}
printlist(plist);
printlist(endlist);
if (docond) {
if (doold)
printf("))");
else
printf(") )");
} else {
printf(")");
}
if (!dodiff)
printf(";\n");
else
printf("\n");
if (dodiff) {
printf("> #else\n");
printf("%lda%ld\n", endline - 1, endline);
printf("> #endif\n");
}
}
/* Get all the function declarations */
void getdecl(f)
FILE *f;
{
Word *plist, *wlist = NULL;
char buf[80];
int sawsomething;
long startline = 0L; /* line where declaration started */
int oktoprint;
again: /* SHAME SHAME */
word_free(wlist);
wlist = word_alloc("");
sawsomething = 0;
oktoprint = 1;
for (;;) {
if (getsym(buf, f) < 0) return;
/* Guess when a declaration is not an external function definition */
if (!strcmp(buf, ",") || !strcmp(buf, "{}") ||
!strcmp(buf, "=") || !strcmp(buf, "typedef") ||
!strcmp(buf, "extern")) {
(void) skipit(buf, f);
goto again;
}
if (!dostatic && !strcmp(buf, "static")) oktoprint = 0;
/* For the benefit of compilers that allow "inline" declarations */
if (!strcmp(buf, "inline") && !sawsomething) continue;
if (!strcmp(buf, ";")) goto again;
/* A left parenthesis *might* indicate a function definition */
if (!strcmp(buf, "(")) {
if (!sawsomething || !(plist = getparamlist(f))) {
(void) skipit(buf, f);
goto again;
}
if (plist == ABORTED) goto again;
/* It seems to have been what we wanted */
if (oktoprint) emit(wlist, plist, startline);
word_free(plist);
goto again;
}
addword(wlist, buf);
if (!sawsomething) startline = symline;
sawsomething = 1;
}
}
int main(argc, argv)
int argc;
char **argv;
{
FILE *f, *g;
char *t;
char newname[40];
progname = argv[0];
argv++;
argc--;
g = stdout;
while (*argv && **argv == '-') {
t = *argv++;
--argc;
t++;
while (*t) {
if (*t == 's')
dostatic = 1;
else if (*t == 'n')
donum = 1;
else if (*t == 'p')
docond = 0;
else if (*t == 'P')
doold =1;
else if (*t == 'd') {
dodiff = 1;
doold = 1;
docond = 0;
donum = 0;
dostatic = 1;
} else
Usage();
t++;
}
}
if (docond && doold) {
printf("#ifdef __STDC__\n");
printf("# define P(args)\targs\n");
printf("#else\n");
printf("# define P(args)\t()\n");
printf("#endif\n\n");
}
if (argc == 0)
getdecl(stdin);
else
while (argc > 0 && *argv) {
if (!(f = fopen(*argv, "r"))) {
perror(*argv);
exit(EXIT_FAILURE);
}
#if 0
if (dodiff) {
(void) sprintf(newname, "%sdif", *argv);
(void) fclose(g);
if (!(g = fopen(newname, "w"))) {
perror(newname);
exit(EXIT_FAILURE);
}
}
#endif
if (doold && dohead && !dodiff) printf("\n/* %s */\n", *argv);
linenum = 1;
newline_seen = 1;
glastc = ' ';
getdecl(f);
argc--;
argv++;
(void) fclose(f);
}
if (docond && doold) printf("\n#undef P\n"); /* clean up namespace */
(void) fclose(g);
return(EXIT_SUCCESS);
}
void Usage()
{
fputs("Usage: ", stderr);
fputs(progname, stderr);
fputs(" [-d][-n][-p][-s] [files ...]\n", stderr);
fputs(" -P: use P() style instead of _PROTOTYPE\n", stderr);
fputs(" -d: produce a diff file to prototype original source\n", stderr);
fputs(" -n: put line numbers of declarations as comments\n", stderr);
fputs(" -p: don't make header files readable by K&R compilers\n", stderr);
fputs(" -s: include declarations for static functions\n", stderr);
exit(EXIT_FAILURE);
}