688 lines
14 KiB
C
Executable file
688 lines
14 KiB
C
Executable file
/* 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);
|
|
}
|