forget difference between big and small commands - obsolete with vm.
[minix.git] / commands / simple / proto.c
blob41a364092e24182613ebfd7de62baee03e0176cd
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.
7 */
8 #define EXIT_SUCCESS 0
9 #define EXIT_FAILURE 1
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdlib.h>
14 #include <stdio.h>
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 */
20 typedef struct word {
21 struct word *next;
22 char string[1];
23 } Word;
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. */
63 Word *word_alloc(s)
64 char *s;
66 Word *w;
68 w = (Word *) malloc(sizeof(Word) + strlen(s) + 1);
69 if (w == NULL) {
70 fprintf(stderr, "%s: out of memory\n", progname);
71 exit(1);
73 (void) strcpy(w->string, s);
74 w->next = NULL;
75 return w;
78 void word_free(w)
79 Word *w;
81 Word *oldw;
82 while (w) {
83 oldw = w;
84 w = w->next;
85 free((char *) oldw);
89 /* Return the length of a list; empty words are not counted */
90 int List_len(w)
91 Word *w;
93 int count = 0;
95 while (w) {
96 if (*w->string) count++;
97 w = w->next;
99 return count;
102 /* Append two lists, and return the result */
103 Word *word_append(w1, w2)
104 Word *w1, *w2;
106 Word *r, *w;
108 r = w = word_alloc("");
110 while (w1) {
111 w->next = word_alloc(w1->string);
112 w = w->next;
113 w1 = w1->next;
115 while (w2) {
116 w->next = word_alloc(w2->string);
117 w = w->next;
118 w2 = w2->next;
121 return r;
124 /* See if the last entry in w2 is in w1 */
125 int foundin(w1, w2)
126 Word *w1, *w2;
128 while (w2->next) w2 = w2->next;
130 while (w1) {
131 if (!strcmp(w1->string, w2->string)) return 1;
132 w1 = w1->next;
134 return 0;
137 /* Add the string s to the given list of words */
138 void addword(w, s)
139 Word *w;
140 char *s;
142 while (w->next) w = w->next;
143 w->next = word_alloc(s);
146 /* Printlist: print out a list */
147 void printlist(p)
148 Word *p;
150 Word *w;
151 int i = 0;
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(" ");
157 i++;
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".
165 Word *typelist(p)
166 Word *p;
168 Word *w, *r, *last;
170 last = r = w = word_alloc("");
171 while (p && p->next) {
172 if (p->string[0] == '[') {
173 word_free(w);
174 last->next = NULL;
175 break;
177 if (p->string[0] && !ISCSYM(p->string[0])) break;
178 w->next = word_alloc(p->string);
179 last = w;
180 w = w->next;
181 p = p->next;
183 return r;
186 /* Typefixhack: promote formal parameters of type "char", "unsigned char",
187 * "short", or "unsigned short" to "int".
189 void typefixhack(w)
190 Word *w;
192 Word *oldw = 0;
194 while (w) {
195 if (*w->string) {
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;
201 free((char *) w);
202 w = oldw;
204 (void) strcpy(w->string, "int");
207 w = w->next;
211 /* Read a character: if it's a newline, increment the line count */
212 int ngetc(f)
213 FILE *f;
215 int c;
217 c = getc(f);
218 if (c == '\n') linenum++;
220 return c;
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
225 * to a blank.
227 int fnextch(f)
228 FILE *f;
230 int c, lastc, incomment;
232 c = ngetc(f);
233 while (c == '\\') {
234 c = ngetc(f); /* skip a character */
235 c = ngetc(f);
237 if (c == '/' && !inquote) {
238 c = ngetc(f);
239 if (c == '*') {
240 incomment = 1;
241 c = ' ';
242 while (incomment) {
243 lastc = c;
244 c = ngetc(f);
245 if (lastc == '*' && c == '/')
246 incomment = 0;
247 else if (c < 0)
248 return c;
250 return fnextch(f);
251 } else {
252 if (c == '\n') linenum--;
253 (void) ungetc(c, f);
254 return '/';
257 return c;
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.
264 int nextch(f)
265 FILE *f;
267 int c;
269 c = fnextch(f);
270 if (newline_seen && c == '#') {
271 do {
272 c = fnextch(f);
273 } while (c >= 0 && c != '\n');
274 if (c < 0) return c;
276 newline_seen = (c == '\n');
278 if (c == '\'' || c == '\"') {
279 inquote = c;
280 while ((c = fnextch(f)) >= 0) {
281 if (c == inquote) {
282 inquote = 0;
283 return '0';
287 return c;
290 /* Get the next symbol from the file, skipping blanks.
291 * Return 0 if OK, -1 for EOF.
292 * Also collapses everything between { and }
294 int getsym(buf, f)
295 char *buf;
296 FILE *f;
298 register int c;
299 int inbrack = 0;
301 c = glastc;
302 while ((c > 0) && isspace(c)) c = nextch(f);
303 if (c < 0) return -1;
304 if (c == '{') {
305 inbrack = 1;
306 endline = linenum;
307 while (inbrack) {
308 c = nextch(f);
309 if (c < 0) {
310 glastc = c;
311 return c;
313 if (c == '{')
314 inbrack++;
315 else if (c == '}')
316 inbrack--;
318 (void) strcpy(buf, "{}");
319 glastc = nextch(f);
320 return 0;
322 if (!ISCSYM(c)) {
323 *buf++ = c;
324 glastc = nextch(f);
325 if (c == '(' && glastc == '*') { /* Look for a 'f'n pointer */
326 *buf++ = glastc;
327 glastc = nextch(f);
329 *buf = 0;
330 return 0;
332 symline = linenum;
333 while (ISCSYM(c)) {
334 *buf++ = c;
335 c = nextch(f);
337 *buf = 0;
338 glastc = c;
339 return 0;
343 /* Skipit: skip until a ";" or the end of a function declaration is seen */
344 int skipit(buf, f)
345 char *buf;
346 FILE *f;
348 int i;
350 do {
351 i = getsym(buf, f);
352 if (i < 0) return i;
353 } while (*buf != ';' && *buf != '{');
355 return 0;
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)
362 FILE *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 */
370 int sawsomething;
371 int i;
372 int inparen = 0;
373 char buf[80];
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 */
383 for (;;) {
384 if (getsym(buf, f) < 0) return(NULL);
385 if (*buf == ')' && (--inparen < 0)) {
386 if (sawsomething) { /* if we've seen an arg */
387 pname[np] = plist;
388 plist = word_alloc("");
389 np++;
391 break;
393 if (*buf == ';') { /* something weird */
394 return ABORTED;
396 sawsomething = 1; /* there's something in the arg. list */
397 if (*buf == ',' && inparen == 0) {
398 pname[np] = plist;
399 plist = word_alloc("");
400 np++;
401 } else {
402 addword(plist, buf);
403 if (*buf == '(') inparen++;
407 /* Next, get the declarations after the function header */
408 inparen = 0;
409 tlist = word_alloc("");
410 plist = word_alloc("");
411 tlistdone = 0;
412 sawsomething = 0;
413 for (;;) {
414 if (getsym(buf, f) < 0) return(NULL);
416 /* Handle parentheses, which should indicate func pointer rtn values */
417 if (*buf == '(') {
418 addword(endlist, buf);
419 addword(endlist, " void ");
420 inparen++;
421 } else if (*buf == ')') {
422 if (symline == linenum) {
423 addword(endlist, buf);
424 addword(endlist, buf);
426 inparen--;
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])) {
432 typed[i] = 1;
433 word_free(pname[i]);
434 pname[i] = word_append(tlist, plist);
435 /* Promote types */
436 typefixhack(pname[i]);
437 break;
440 if (!tlistdone) {
441 tlist = typelist(plist);
442 tlistdone = 1;
444 word_free(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])) {
451 typed[i] = 1;
452 word_free(pname[i]);
453 pname[i] = word_append(tlist, plist);
454 typefixhack(pname[i]);
455 break;
458 tlistdone = 0;
459 word_free(tlist);
460 word_free(plist);
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")) {
467 sawsomething = 1;
468 addword(plist, buf);
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, ", ");
491 return plist;
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)
498 Word *wlist, *plist;
499 long startline;
501 Word *w;
502 int count = 0;
504 if (doold == 0) printf("_PROTOTYPE( ");
505 if (dodiff) {
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 ");
514 printlist(wlist);
515 if (docond) {
516 if (doold)
517 printf(" P((");
518 else
519 printf(", (");
520 } else {
521 printf("(");
524 printlist(plist);
525 printlist(endlist);
527 if (docond) {
528 if (doold)
529 printf("))");
530 else
531 printf(") )");
532 } else {
533 printf(")");
536 if (!dodiff)
537 printf(";\n");
538 else
539 printf("\n");
541 if (dodiff) {
542 printf("> #else\n");
543 printf("%lda%ld\n", endline - 1, endline);
544 printf("> #endif\n");
548 /* Get all the function declarations */
549 void getdecl(f)
550 FILE *f;
552 Word *plist, *wlist = NULL;
553 char buf[80];
554 int sawsomething;
555 long startline = 0L; /* line where declaration started */
556 int oktoprint;
558 again: /* SHAME SHAME */
559 word_free(wlist);
560 wlist = word_alloc("");
561 sawsomething = 0;
562 oktoprint = 1;
564 for (;;) {
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);
572 goto again;
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);
584 goto again;
586 if (plist == ABORTED) goto again;
588 /* It seems to have been what we wanted */
589 if (oktoprint) emit(wlist, plist, startline);
590 word_free(plist);
591 goto again;
593 addword(wlist, buf);
594 if (!sawsomething) startline = symline;
595 sawsomething = 1;
599 int main(argc, argv)
600 int argc;
601 char **argv;
603 FILE *f, *g;
604 char *t;
605 char newname[40];
607 progname = argv[0];
608 argv++;
609 argc--;
610 g = stdout;
612 while (*argv && **argv == '-') {
613 t = *argv++;
614 --argc;
615 t++;
616 while (*t) {
617 if (*t == 's')
618 dostatic = 1;
619 else if (*t == 'n')
620 donum = 1;
621 else if (*t == 'p')
622 docond = 0;
623 else if (*t == 'P')
624 doold =1;
625 else if (*t == 'd') {
626 dodiff = 1;
627 doold = 1;
628 docond = 0;
629 donum = 0;
630 dostatic = 1;
631 } else
632 Usage();
633 t++;
637 if (docond && doold) {
638 printf("#ifdef __STDC__\n");
639 printf("# define P(args)\targs\n");
640 printf("#else\n");
641 printf("# define P(args)\t()\n");
642 printf("#endif\n\n");
644 if (argc == 0)
645 getdecl(stdin);
646 else
647 while (argc > 0 && *argv) {
648 if (!(f = fopen(*argv, "r"))) {
649 perror(*argv);
650 exit(EXIT_FAILURE);
652 #if 0
653 if (dodiff) {
654 (void) sprintf(newname, "%sdif", *argv);
655 (void) fclose(g);
656 if (!(g = fopen(newname, "w"))) {
657 perror(newname);
658 exit(EXIT_FAILURE);
661 #endif
662 if (doold && dohead && !dodiff) printf("\n/* %s */\n", *argv);
663 linenum = 1;
664 newline_seen = 1;
665 glastc = ' ';
666 getdecl(f);
667 argc--;
668 argv++;
669 (void) fclose(f);
671 if (docond && doold) printf("\n#undef P\n"); /* clean up namespace */
672 (void) fclose(g);
673 return(EXIT_SUCCESS);
677 void Usage()
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);
687 exit(EXIT_FAILURE);