1 /* proto - Generate ANSI C prototypes. Author: Eric R. Smith */
3 /* Program to extract function declarations from C source code
4 * Written by Eric R. Smith and placed in the public domain
5 * Thanks are due to Jwahar R. Bammi for fixing several bugs
6 * And providing the Unix makefiles.
16 #define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_' ))
17 #define ABORTED ( (Word *) -1 )
18 #define MAXPARAM 20 /* max. number of parameters to a function */
25 int inquote
= 0; /* in a quote? */
26 int newline_seen
= 1; /* are we at the start of a line */
27 long linenum
= 1L; /* line number in current file */
28 long endline
= 0L; /* the last line before the { of a f'n */
29 long symline
= 0L; /* Line that symbol was on, set by getsym() */
30 int dostatic
= 0; /* do static functions? */
31 int donum
= 0; /* print line numbers? */
32 int dohead
= 1; /* do file headers? */
33 int docond
= 1; /* conditionalize for non-ANSI compilers? */
34 int dodiff
= 0; /* Output a diff file to prototype original */
35 int doold
= 0; /* do old style: P() */
36 int glastc
= ' '; /* last char. seen by getsym() */
37 Word
*endlist
; /* Parentheses after the parameters */
38 char *progname
; /* name of program (for error messages) */
41 _PROTOTYPE(Word
* word_alloc
, (char *s
));
42 _PROTOTYPE(void word_free
, (Word
* w
));
43 _PROTOTYPE(int List_len
, (Word
* w
));
44 _PROTOTYPE(Word
* word_append
, (Word
* w1
, Word
* w2
));
45 _PROTOTYPE(int foundin
, (Word
* w1
, Word
* w2
));
46 _PROTOTYPE(void addword
, (Word
* w
, char *s
));
47 _PROTOTYPE(void printlist
, (Word
* p
));
48 _PROTOTYPE(Word
* typelist
, (Word
* p
));
49 _PROTOTYPE(void typefixhack
, (Word
* w
));
50 _PROTOTYPE(int ngetc
, (FILE * f
));
51 _PROTOTYPE(int fnextch
, (FILE * f
));
52 _PROTOTYPE(int nextch
, (FILE * f
));
53 _PROTOTYPE(int getsym
, (char *buf
, FILE * f
));
54 _PROTOTYPE(int skipit
, (char *buf
, FILE * f
));
55 _PROTOTYPE(Word
* getparamlist
, (FILE * f
));
56 _PROTOTYPE(void emit
, (Word
* wlist
, Word
* plist
, long startline
));
57 _PROTOTYPE(void getdecl
, (FILE * f
));
58 _PROTOTYPE(int main
, (int argc
, char **argv
));
59 _PROTOTYPE(void Usage
, (void));
61 /* Routines for manipulating lists of words. */
68 w
= (Word
*) malloc(sizeof(Word
) + strlen(s
) + 1);
70 fprintf(stderr
, "%s: out of memory\n", progname
);
73 (void) strcpy(w
->string
, s
);
89 /* Return the length of a list; empty words are not counted */
96 if (*w
->string
) count
++;
102 /* Append two lists, and return the result */
103 Word
*word_append(w1
, w2
)
108 r
= w
= word_alloc("");
111 w
->next
= word_alloc(w1
->string
);
116 w
->next
= word_alloc(w2
->string
);
124 /* See if the last entry in w2 is in w1 */
128 while (w2
->next
) w2
= w2
->next
;
131 if (!strcmp(w1
->string
, w2
->string
)) return 1;
137 /* Add the string s to the given list of words */
142 while (w
->next
) w
= w
->next
;
143 w
->next
= word_alloc(s
);
146 /* Printlist: print out a list */
153 for (w
= p
; w
; w
= w
->next
) {
154 printf("%s", w
->string
);
155 if (ISCSYM(w
->string
[0]) && i
> 0
156 && w
->next
&& w
->next
->string
[0] != ',') printf(" ");
161 /* Given a list representing a type and a variable name, extract just
162 * the base type, e.g. "struct word *x" would yield "struct word".
163 * Similarly, "unsigned char x[]" would yield "unsigned char".
170 last
= r
= w
= word_alloc("");
171 while (p
&& p
->next
) {
172 if (p
->string
[0] == '[') {
177 if (p
->string
[0] && !ISCSYM(p
->string
[0])) break;
178 w
->next
= word_alloc(p
->string
);
186 /* Typefixhack: promote formal parameters of type "char", "unsigned char",
187 * "short", or "unsigned short" to "int".
196 if ((!strcmp(w
->string
, "char") ||
197 !strcmp(w
->string
, "short"))
198 && (List_len(w
->next
) < 2)) {
199 if (oldw
&& !strcmp(oldw
->string
, "unsigned")) {
200 oldw
->next
= w
->next
;
204 (void) strcpy(w
->string
, "int");
211 /* Read a character: if it's a newline, increment the line count */
218 if (c
== '\n') linenum
++;
223 /* Read the next character from the file. If the character is '\' then
224 * read and skip the next character. Any comment sequence is converted
230 int c
, lastc
, incomment
;
234 c
= ngetc(f
); /* skip a character */
237 if (c
== '/' && !inquote
) {
245 if (lastc
== '*' && c
== '/')
252 if (c
== '\n') linenum
--;
261 /* Get the next "interesting" character. Comments are skipped, and strings
262 * are converted to "0". Also, if a line starts with "#" it is skipped.
270 if (newline_seen
&& c
== '#') {
273 } while (c
>= 0 && c
!= '\n');
276 newline_seen
= (c
== '\n');
278 if (c
== '\'' || c
== '\"') {
280 while ((c
= fnextch(f
)) >= 0) {
290 /* Get the next symbol from the file, skipping blanks.
291 * Return 0 if OK, -1 for EOF.
292 * Also collapses everything between { and }
302 while ((c
> 0) && isspace(c
)) c
= nextch(f
);
303 if (c
< 0) return -1;
318 (void) strcpy(buf
, "{}");
325 if (c
== '(' && glastc
== '*') { /* Look for a 'f'n pointer */
343 /* Skipit: skip until a ";" or the end of a function declaration is seen */
353 } while (*buf
!= ';' && *buf
!= '{');
358 /* Get a parameter list; when this is called the next symbol in line
359 * should be the first thing in the list.
361 Word
*getparamlist(f
)
364 static Word
*pname
[MAXPARAM
]; /* parameter names */
365 Word
*tlist
, /* type name */
366 *plist
; /* temporary */
367 int np
= 0; /* number of parameters */
368 int typed
[MAXPARAM
]; /* parameter has been given a type */
369 int tlistdone
; /* finished finding the type name */
375 for (i
= 0; i
< MAXPARAM
; i
++) typed
[i
] = 0;
377 plist
= word_alloc("");
378 endlist
= word_alloc("");
380 /* First, get the stuff inside brackets (if anything) */
382 sawsomething
= 0; /* gets set nonzero when we see an arg */
384 if (getsym(buf
, f
) < 0) return(NULL
);
385 if (*buf
== ')' && (--inparen
< 0)) {
386 if (sawsomething
) { /* if we've seen an arg */
388 plist
= word_alloc("");
393 if (*buf
== ';') { /* something weird */
396 sawsomething
= 1; /* there's something in the arg. list */
397 if (*buf
== ',' && inparen
== 0) {
399 plist
= word_alloc("");
403 if (*buf
== '(') inparen
++;
407 /* Next, get the declarations after the function header */
409 tlist
= word_alloc("");
410 plist
= word_alloc("");
414 if (getsym(buf
, f
) < 0) return(NULL
);
416 /* Handle parentheses, which should indicate func pointer rtn values */
418 addword(endlist
, buf
);
419 addword(endlist
, " void ");
421 } else if (*buf
== ')') {
422 if (symline
== linenum
) {
423 addword(endlist
, buf
);
424 addword(endlist
, buf
);
427 } else if (*buf
== ',' && !inparen
) {
428 /* Handle a list like "int x,y,z" */
429 if (!sawsomething
) return(NULL
);
430 for (i
= 0; i
< np
; i
++) {
431 if (!typed
[i
] && foundin(plist
, pname
[i
])) {
434 pname
[i
] = word_append(tlist
, plist
);
436 typefixhack(pname
[i
]);
441 tlist
= typelist(plist
);
445 plist
= word_alloc("");
446 } else if (*buf
== ';') {
447 /* Handle the end of a list */
448 if (!sawsomething
) return ABORTED
;
449 for (i
= 0; i
< np
; i
++) {
450 if (!typed
[i
] && foundin(plist
, pname
[i
])) {
453 pname
[i
] = word_append(tlist
, plist
);
454 typefixhack(pname
[i
]);
461 tlist
= word_alloc("");
462 plist
= word_alloc("");
463 } else if (!strcmp(buf
, "{}"))
464 break; /* Handle the beginning of the function */
465 /* Otherwise, throw word into list (except for "register") */
466 else if (strcmp(buf
, "register")) {
469 if (*buf
== '(') inparen
++;
470 if (*buf
== ')') inparen
--;
474 /* Now take the info we have and build a prototype list */
476 /* Empty parameter list means "void" */
477 if (np
== 0) return word_alloc("void");
479 plist
= tlist
= word_alloc("");
480 for (i
= 0; i
< np
; i
++) {
482 /* If no type provided, make it an "int" */
483 if (!(pname
[i
]->next
) ||
484 (!(pname
[i
]->next
->next
)&&strcmp(pname
[i
]->next
->string
,"void"))) {
485 addword(tlist
, "int");
487 while (tlist
->next
) tlist
= tlist
->next
;
488 tlist
->next
= pname
[i
];
489 if (i
< np
- 1) addword(tlist
, ", ");
494 /* Emit a function declaration. The attributes and name of the function
495 * are in wlist; the parameters are in plist.
497 void emit(wlist
, plist
, startline
)
504 if (doold
== 0) printf("_PROTOTYPE( ");
506 printf("%lda%ld,%ld\n", startline
- 1, startline
, startline
+2);
507 printf("> #ifdef __STDC__\n> ");
509 if (donum
) printf("/*%8ld */ ", startline
);
510 for (w
= wlist
; w
; w
= w
->next
) {
511 if (w
->string
[0]) count
++;
513 if (count
< 2) printf("int ");
543 printf("%lda%ld\n", endline
- 1, endline
);
544 printf("> #endif\n");
548 /* Get all the function declarations */
552 Word
*plist
, *wlist
= NULL
;
555 long startline
= 0L; /* line where declaration started */
558 again
: /* SHAME SHAME */
560 wlist
= word_alloc("");
565 if (getsym(buf
, f
) < 0) return;
567 /* Guess when a declaration is not an external function definition */
568 if (!strcmp(buf
, ",") || !strcmp(buf
, "{}") ||
569 !strcmp(buf
, "=") || !strcmp(buf
, "typedef") ||
570 !strcmp(buf
, "extern")) {
571 (void) skipit(buf
, f
);
574 if (!dostatic
&& !strcmp(buf
, "static")) oktoprint
= 0;
576 /* For the benefit of compilers that allow "inline" declarations */
577 if (!strcmp(buf
, "inline") && !sawsomething
) continue;
578 if (!strcmp(buf
, ";")) goto again
;
580 /* A left parenthesis *might* indicate a function definition */
581 if (!strcmp(buf
, "(")) {
582 if (!sawsomething
|| !(plist
= getparamlist(f
))) {
583 (void) skipit(buf
, f
);
586 if (plist
== ABORTED
) goto again
;
588 /* It seems to have been what we wanted */
589 if (oktoprint
) emit(wlist
, plist
, startline
);
594 if (!sawsomething
) startline
= symline
;
612 while (*argv
&& **argv
== '-') {
625 else if (*t
== 'd') {
637 if (docond
&& doold
) {
638 printf("#ifdef __STDC__\n");
639 printf("# define P(args)\targs\n");
641 printf("# define P(args)\t()\n");
642 printf("#endif\n\n");
647 while (argc
> 0 && *argv
) {
648 if (!(f
= fopen(*argv
, "r"))) {
654 (void) sprintf(newname
, "%sdif", *argv
);
656 if (!(g
= fopen(newname
, "w"))) {
662 if (doold
&& dohead
&& !dodiff
) printf("\n/* %s */\n", *argv
);
671 if (docond
&& doold
) printf("\n#undef P\n"); /* clean up namespace */
673 return(EXIT_SUCCESS
);
679 fputs("Usage: ", stderr
);
680 fputs(progname
, stderr
);
681 fputs(" [-d][-n][-p][-s] [files ...]\n", stderr
);
682 fputs(" -P: use P() style instead of _PROTOTYPE\n", stderr
);
683 fputs(" -d: produce a diff file to prototype original source\n", stderr
);
684 fputs(" -n: put line numbers of declarations as comments\n", stderr
);
685 fputs(" -p: don't make header files readable by K&R compilers\n", stderr
);
686 fputs(" -s: include declarations for static functions\n", stderr
);