home *** CD-ROM | disk | FTP | other *** search
- /* 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. */
-
- #if defined(__STDC__) && !defined(minix)
- #include <stddef.h>
- #include <stdlib.h>
- #else
- extern char *malloc();
- #endif
- #ifndef EXIT_SUCCESS
- #define EXIT_SUCCESS 0
- #endif
- #ifndef EXIT_FAILURE
- #define EXIT_FAILURE 1
- #endif
-
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
-
- /*#define DEBUG(s) (fputs(s, stderr)) /* */
- #define DEBUG(s) /* */
-
- #define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_'))
- #define ABORTED ( (Word *) -1 )
- #define MAXPARAM 20 /* max. number of parameters to a function */
- #define NEWBUFSIZ (20480*sizeof(char)) /* new buffer size */
-
- 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 */
- 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 glastc = ' '; /* last char. seen by getsym() */
- FILE *outf;
-
- typedef struct word {
- struct word *next;
- char string[1];
- } Word;
-
- #include "mkproto.h"
-
- /*
- * Routines for manipulating lists of words.
- */
-
- Word *word_alloc(s)
- char *s;
- {
- Word *w;
-
- w = (Word *) malloc(sizeof(Word) + strlen(s) + 1); /* ++jrb */
- 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(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);
- }
-
- /* given a list representing a type and a variable name, extract just
- * the base type, e.g. "struct word *x" would yield "struct word"
- */
-
- Word *typelist(p)
- Word *p;
- {
- Word *w, *r;
-
- r = w = word_alloc("");
- while (p && p->next) {
- if (p->string[0] && !ISCSYM(p->string[0]))
- break;
- w->next = word_alloc(p->string);
- 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(w);
- w = oldw;
- }
- strcpy(w->string, "int");
- }
- }
- w = w->next;
- }
- }
-
- /* read a character: if it's a newline, increment the line count */
-
- #ifdef __GNUC__ /* ++jrb */
- inline
- #endif
- 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 == '\\') {
- DEBUG("fnextch: in backslash loop\n");
- c = ngetc(f); /* skip a character */
- c = ngetc(f);
- }
- if (c == '/' && !inquote) {
- c = ngetc(f);
- if (c == '*') {
- incomment = 1;
- c = ' ';
- DEBUG("fnextch: comment seen\n");
- 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--;
- 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 == '\"') {
- DEBUG("nextch: in a quote\n");
- inquote = c;
- while ( (c = fnextch(f)) >= 0 ) {
- if (c == inquote) {
- inquote = 0;
- DEBUG("nextch: out of quote\n");
- return '0';
- }
- }
- DEBUG("nextch: EOF in a quote\n");
- }
- 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;
-
- DEBUG("in getsym\n");
- c = glastc;
- while ((c > 0) && isspace(c)) {
- c = nextch(f);
- }
- DEBUG("getsym: spaces skipped\n");
- if (c < 0) {
- DEBUG("EOF read in getsym\n");
- return -1;
- }
- if (c == '{') {
- inbrack = 1;
- DEBUG("getsym: in bracket\n");
- while (inbrack) {
- c = nextch(f);
- if (c < 0) {
- DEBUG("getsym: EOF seen in bracket loop\n");
- glastc = c;
- return c;
- }
- if (c == '{') inbrack++;
- else if (c == '}') inbrack--;
- }
- strcpy(buf, "{}");
- glastc = nextch(f);
- DEBUG("getsym: out of in bracket loop\n");
- return 0;
- }
- if (!ISCSYM(c)) {
- *buf++ = c;
- *buf = 0;
- glastc = nextch(f);
- DEBUG("getsym: returning special symbol\n");
- return 0;
- }
- while (ISCSYM(c)) {
- *buf++ = c;
- c = nextch(f);
- }
- *buf = 0;
- glastc = c;
- DEBUG("getsym: returning word\n");
- 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 {
- DEBUG("in skipit loop\n");
- 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];
-
- DEBUG("in getparamlist\n");
- for (i = 0; i < MAXPARAM; i++)
- typed[i] = 0;
-
- plist = 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 a list like "int x,y,z" */
- if (*buf == ',' && !inparen) {
- 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("");
- }
- /* handle the end of a list */
- else if (*buf == ';') {
- 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("");
- }
- /* handle the beginning of the function */
- else if (!strcmp(buf, "{}")) break;
- /* otherwise, throw the word into the 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;
-
- DEBUG("emit called\n");
- if (donum)
- fprintf(outf, "/*%8ld */ ", startline);
-
- for (w = wlist; w; w = w->next) {
- if (w->string[0])
- count ++;
- }
-
- if (count < 2)
- fprintf(outf, "int ");
-
- for (w = wlist; w; w = w->next) {
- fprintf(outf, "%s", w->string);
- if (ISCSYM(w->string[0]))
- fprintf(outf, " ");
- }
- if (docond)
- fprintf(outf, "P((");
- else
- fprintf(outf, "( ");
- for (w = plist; w; w = w->next) {
- fprintf(outf, "%s", w->string);
- if (ISCSYM(w->string[0]))
- fprintf(outf, " ");
- }
- if (docond)
- fprintf(outf, "));\n");
- else
- fprintf(outf, ");\n");
- }
-
- /*
- * get all the function declarations
- */
-
- void getdecl(f)
- FILE *f;
- {
- Word *plist, *wlist = NULL;
- char buf[80];
- int sawsomething;
- long startline; /* line where declaration started */
- int oktoprint;
- again:
- word_free(wlist);
- wlist = word_alloc("");
- sawsomething = 0;
- oktoprint = 1;
-
- for(;;) {
- DEBUG("main getdecl loop\n");
- if (getsym(buf,f) < 0) {
- DEBUG("EOF in getdecl loop\n");
- return;
- }
- /* try to guess when a declaration is not an external function definition */
- if (!strcmp(buf, ",") || !strcmp(buf, "{}") ||
- !strcmp(buf, "=") || !strcmp(buf, "typedef") ||
- !strcmp(buf, "extern")) {
- 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, "(")) {
- startline = linenum;
- if (!sawsomething || !(plist = getparamlist(f))) {
- 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);
- sawsomething = 1;
- }
- }
-
- void
- main(argc, argv)
- int argc; char **argv;
- {
- FILE *f;
- char *t, *iobuf;
- extern void Usage();
-
- outf=stdout;
- argv++; argc--;
-
- iobuf = malloc(NEWBUFSIZ);
- 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 == 'o') {
- if (!(outf=fopen(*argv, "w"))) {
- perror(*argv);
- exit(1);
- }
- ++argv;
- --argc;
- } else
- Usage();
- t++;
- }
- }
-
- if (docond) {
- fprintf(outf, "#ifdef __STDC__\n");
- fprintf(outf, "# define\tP(s) s\n");
- fprintf(outf, "#else\n");
- fprintf(outf, "# define P(s) ()\n");
- fprintf(outf, "#endif\n\n");
- }
- if (argc == 0)
- getdecl(stdin);
- else
- while (argc > 0 && *argv) {
- DEBUG("trying a new file\n");
- if (!(f = fopen(*argv, "r"))) {
- perror(*argv);
- exit(EXIT_FAILURE);
- }
- if (iobuf)
- setvbuf(f, iobuf, _IOFBF, NEWBUFSIZ);
- if (dohead)
- fprintf(outf, "\n/* %s */\n", *argv);
- linenum = 1;
- newline_seen = 1;
- glastc = ' ';
- DEBUG("calling getdecl\n");
- getdecl(f);
- DEBUG("back from getdecl\n");
- argc--; argv++;
- fclose(f);
- DEBUG("back from fclose\n");
- }
- if (docond) {
- fprintf(outf, "\n#undef P\n"); /* clean up namespace */
- }
- exit(EXIT_SUCCESS);
- }
-
-
- void Usage()
- {
- fputs("Usage: mkproto [-n][-s][-p][-o outfile][files ...]\n",stderr);
- fputs(" -o: write output to outfile\n", stderr);
- fputs(" -n: put line numbers of declarations as comments\n",stderr);
- fputs(" -s: include declarations for static functions\n", stderr);
- fputs(" -p: don't make header files readable by non-ANSI compilers\n",
- stderr);
- exit(EXIT_FAILURE);
- }
-