home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 419b.lha / mkproto / mkproto.c < prev    next >
C/C++ Source or Header  |  1990-04-01  |  13KB  |  663 lines

  1. /* Program to extract function declarations from C source code */
  2. /* Written by Eric R. Smith and placed in the public domain    */
  3. /* Thanks are due to Jwahar R. Bammi for fixing several bugs   */
  4. /* and providing the Unix makefiles.                           */
  5.  
  6. #if defined(__STDC__) && !defined(minix)
  7. #include <stddef.h>
  8. #include <stdlib.h>
  9. #else
  10. #define EXIT_SUCCESS  0
  11. #define EXIT_FAILURE  1
  12. extern char *malloc();
  13. #endif
  14.  
  15. #if defined(AMIGA)
  16. #define EXIT_SUCCESS  0
  17. #define EXIT_FAILURE  1
  18. #endif
  19.  
  20. #include <stdio.h>
  21. #include <ctype.h>
  22. #include <string.h>
  23.  
  24. /*#define DEBUG(s) (fputs(s, stderr)) /* */
  25. #define DEBUG(s) /* */
  26.  
  27. #define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_'))
  28. #define ABORTED ( (Word *) -1 )
  29. #define MAXPARAM 20         /* max. number of parameters to a function */
  30. #define NEWBUFSIZ (20480*sizeof(char)) /* new buffer size */
  31.  
  32. int inquote = 0;        /* in a quote?? */
  33. int newline_seen = 1;        /* are we at the start of a line */
  34. long linenum  = 1L;        /* line number in current file */
  35. int dostatic = 0;        /* do static functions? */
  36. int donum    = 0;        /* print line numbers? */
  37. int dohead   = 1;        /* do file headers? */
  38. int docond   = 1;        /* conditionalize for non-ANSI compilers? */
  39. int glastc   = ' ';        /* last char. seen by getsym() */
  40.  
  41. typedef struct word {
  42.     struct word *next;
  43.     char   string[1];
  44. } Word;
  45.  
  46. #include "mkproto.h"
  47.  
  48. /*
  49.  * Routines for manipulating lists of words.
  50.  */
  51.  
  52. Word *word_alloc(s)
  53.     char *s;
  54. {
  55.     Word *w;
  56.  
  57.     w = (Word *) malloc(sizeof(Word) + strlen(s) + 1); /* ++jrb */
  58.     strcpy(w->string, s);
  59.     w->next = NULL;
  60.     return w;
  61. }
  62.  
  63. void word_free(w)
  64.     Word *w;
  65. {
  66.     Word *oldw;
  67.     while (w) {
  68.         oldw = w;
  69.         w = w->next;
  70.         free(oldw);
  71.     }
  72. }
  73.  
  74. /* return the length of a list; empty words are not counted */
  75. int
  76. List_len(w)
  77.     Word *w;
  78. {
  79.     int count = 0;
  80.  
  81.     while (w) {
  82.         if (*w->string) count++;
  83.         w = w->next;
  84.     }
  85.     return count;
  86. }
  87.  
  88. /* Append two lists, and return the result */
  89.  
  90. Word *word_append(w1, w2)
  91.     Word *w1, *w2;
  92. {
  93.     Word *r, *w;
  94.  
  95.     r = w = word_alloc("");
  96.  
  97.     while (w1) {
  98.         w->next = word_alloc(w1->string);
  99.         w = w->next;
  100.         w1 = w1->next;
  101.     }
  102.     while (w2) {
  103.         w->next = word_alloc(w2->string);
  104.         w = w->next;
  105.         w2 = w2->next;
  106.     }
  107.  
  108.     return r;
  109. }
  110.     
  111. /* see if the last entry in w2 is in w1 */
  112.  
  113. int
  114. foundin(w1, w2)
  115.     Word *w1, *w2;
  116. {
  117.     while (w2->next)
  118.         w2 = w2->next;
  119.  
  120.     while (w1) {
  121.         if (!strcmp(w1->string, w2->string))
  122.             return 1;
  123.         w1 = w1->next;
  124.     }
  125.     return 0;
  126. }
  127.  
  128. /* add the string s to the given list of words */
  129.  
  130. void addword(w, s)
  131.     Word *w; char *s;
  132. {
  133.     while (w->next) w = w->next;
  134.     w->next = word_alloc(s);
  135. }
  136.  
  137. /* given a list representing a type and a variable name, extract just
  138.  * the base type, e.g. "struct word *x" would yield "struct word"
  139.  */
  140.  
  141. Word *typelist(p)
  142.     Word *p;
  143. {
  144.     Word *w, *r;
  145.  
  146.     r = w = word_alloc("");
  147.     while (p && p->next) {
  148.         if (p->string[0] && !ISCSYM(p->string[0]))
  149.             break;
  150.         w->next = word_alloc(p->string);
  151.         w = w->next;
  152.         p = p->next;
  153.     }
  154.     return r;
  155. }
  156.  
  157. /* typefixhack: promote formal parameters of type "char", "unsigned char",
  158.    "short", or "unsigned short" to "int".
  159. */
  160.  
  161. void typefixhack(w)
  162.     Word *w;
  163. {
  164.     Word *oldw = 0;
  165.  
  166.     while (w) {
  167.         if (*w->string) {
  168.             if ( (!strcmp(w->string, "char") ||
  169.                   !strcmp(w->string, "short") )
  170.                 && (List_len(w->next) < 2) )
  171.             {
  172.                 if (oldw && !strcmp(oldw->string, "unsigned")) {
  173.                     oldw->next = w->next;
  174.                     free(w);
  175.                     w = oldw;
  176.                 }
  177.                 strcpy(w->string, "int");
  178.             }
  179.         }
  180.         w = w->next;
  181.     }
  182. }
  183.  
  184. /* read a character: if it's a newline, increment the line count */
  185.  
  186. #ifdef __GNUC__    /* ++jrb */
  187. inline
  188. #endif
  189. int ngetc(f)
  190.     FILE *f;
  191. {
  192.     int c;
  193.  
  194.     c = getc(f);
  195.     if (c == '\n') linenum++;
  196.  
  197.     return c;
  198. }
  199.  
  200. /* read the next character from the file. If the character is '\' then
  201.  * read and skip the next character. Any comment sequence is converted
  202.  * to a blank.
  203.  */
  204.  
  205. int fnextch(f)
  206.     FILE *f;
  207. {
  208.     int c, lastc, incomment;
  209.  
  210.     c = ngetc(f);
  211.     while (c == '\\') {
  212. DEBUG("fnextch: in backslash loop\n");
  213.         c = ngetc(f);    /* skip a character */
  214.         c = ngetc(f);
  215.     }
  216.     if (c == '/' && !inquote) {
  217.         c = ngetc(f);
  218.         if (c == '*') {
  219.             incomment = 1;
  220.             c = ' ';
  221. DEBUG("fnextch: comment seen\n");
  222.             while (incomment) {
  223.                 lastc = c;
  224.                 c = ngetc(f);
  225.                 if (lastc == '*' && c == '/')
  226.                     incomment = 0;
  227.                 else if (c < 0)
  228.                     return c;
  229.             }
  230.             return fnextch(f);
  231.         }
  232.         else {
  233.             if (c == '\n') linenum--;
  234.             ungetc(c, f);
  235.             return '/';
  236.         }
  237.     }
  238.     return c;
  239. }
  240.  
  241.  
  242. /* Get the next "interesting" character. Comments are skipped, and strings
  243.  * are converted to "0". Also, if a line starts with "#" it is skipped.
  244.  */
  245.  
  246. int nextch(f)
  247.     FILE *f;
  248. {
  249.     int c;
  250.  
  251.     c = fnextch(f);
  252.     if (newline_seen && c == '#') {
  253.         do {
  254.             c = fnextch(f);
  255.         } while (c >= 0 && c != '\n');
  256.         if (c < 0)
  257.             return c;
  258.     }
  259.     newline_seen = (c == '\n');
  260.  
  261.     if (c == '\'' || c == '\"') {
  262. DEBUG("nextch: in a quote\n");
  263.         inquote = c;
  264.         while ( (c = fnextch(f)) >= 0 ) {
  265.             if (c == inquote) {
  266.                 inquote = 0;
  267. DEBUG("nextch: out of quote\n");
  268.                 return '0';
  269.             }
  270.         }
  271. DEBUG("nextch: EOF in a quote\n");
  272.     }
  273.     return c;
  274. }
  275.  
  276. /*
  277.  * Get the next symbol from the file, skipping blanks.
  278.  * Return 0 if OK, -1 for EOF.
  279.  * Also collapses everything between { and }
  280.  */
  281.  
  282. int
  283. getsym(buf, f)
  284.     char *buf; FILE *f;
  285. {
  286.     register int c;
  287.     int inbrack = 0;
  288.  
  289. DEBUG("in getsym\n");
  290.     c = glastc;
  291.     while ((c > 0) && isspace(c)) {
  292.         c = nextch(f);
  293.     }
  294. DEBUG("getsym: spaces skipped\n");
  295.     if (c < 0) {
  296. DEBUG("EOF read in getsym\n");
  297.         return -1;
  298.     }
  299.     if (c == '{') {
  300.         inbrack = 1;
  301. DEBUG("getsym: in bracket\n");
  302.         while (inbrack) {
  303.             c = nextch(f);
  304.             if (c < 0) {
  305. DEBUG("getsym: EOF seen in bracket loop\n");
  306.                 glastc = c;
  307.                 return c;
  308.             }
  309.             if (c == '{') inbrack++;
  310.             else if (c == '}') inbrack--;
  311.         }
  312.         strcpy(buf, "{}");
  313.         glastc = nextch(f);
  314. DEBUG("getsym: out of in bracket loop\n");
  315.         return 0;
  316.     }
  317.     if (!ISCSYM(c)) {
  318.         *buf++ = c;
  319.         *buf = 0;
  320.         glastc = nextch(f);
  321. DEBUG("getsym: returning special symbol\n");
  322.         return 0;
  323.     }
  324.     while (ISCSYM(c)) {
  325.         *buf++ = c;
  326.         c = nextch(f);
  327.     }
  328.     *buf = 0;
  329.     glastc = c;
  330. DEBUG("getsym: returning word\n");
  331.     return 0;
  332. }
  333.  
  334. /*
  335.  * skipit: skip until a ";" or the end of a function declaration is seen
  336.  */
  337. int skipit(buf, f)
  338.     char *buf;
  339.     FILE *f;
  340. {
  341.     int i;
  342.  
  343.     do {
  344. DEBUG("in skipit loop\n");
  345.         i = getsym(buf, f);
  346.         if (i < 0) return i;
  347.     } while (*buf != ';' && *buf != '{');
  348.  
  349.     return 0;
  350. }
  351.  
  352. /*
  353.  * Get a parameter list; when this is called the next symbol in line
  354.  * should be the first thing in the list.
  355.  */
  356.  
  357. Word *getparamlist(f)
  358.     FILE *f;
  359. {
  360.     static Word *pname[MAXPARAM]; /* parameter names */
  361.     Word    *tlist,        /* type name */
  362.         *plist;        /* temporary */
  363.     int      np = 0;        /* number of parameters */
  364.     int      typed[MAXPARAM];  /* parameter has been given a type */
  365.     int    tlistdone;    /* finished finding the type name */
  366.     int    sawsomething;
  367.     int      i;
  368.     int    inparen = 0;
  369.     char buf[80];
  370.  
  371. DEBUG("in getparamlist\n");
  372.     for (i = 0; i < MAXPARAM; i++)
  373.         typed[i] = 0;
  374.  
  375.     plist = word_alloc("");
  376.  
  377. /* first, get the stuff inside brackets (if anything) */
  378.  
  379.     sawsomething = 0;    /* gets set nonzero when we see an arg */
  380.     for (;;) {
  381.         if (getsym(buf, f) < 0) return NULL;
  382.         if (*buf == ')' && (--inparen < 0)) {
  383.             if (sawsomething) {    /* if we've seen an arg */
  384.                 pname[np] = plist;
  385.                 plist = word_alloc("");
  386.                 np++;
  387.             }
  388.             break;
  389.         }
  390.         if (*buf == ';') {    /* something weird */
  391.             return ABORTED;
  392.         }
  393.         sawsomething = 1;    /* there's something in the arg. list */
  394.         if (*buf == ',' && inparen == 0) {
  395.             pname[np] = plist;
  396.             plist = word_alloc("");
  397.             np++;
  398.         }
  399.         else {
  400.             addword(plist, buf);
  401.             if (*buf == '(') inparen++;
  402.         }
  403.     }
  404.  
  405. /* next, get the declarations after the function header */
  406.  
  407.     inparen = 0;
  408.  
  409.     tlist = word_alloc("");
  410.     plist = word_alloc("");
  411.     tlistdone = 0;
  412.     sawsomething = 0;
  413.     for(;;) {
  414.         if (getsym(buf, f) < 0) return NULL;
  415.  
  416. /* handle a list like "int x,y,z" */
  417.         if (*buf == ',' && !inparen) {
  418.             if (!sawsomething)
  419.                 return NULL;
  420.             for (i = 0; i < np; i++) {
  421.                 if (!typed[i] && foundin(plist, pname[i])) {
  422.                     typed[i] = 1;
  423.                     word_free(pname[i]);
  424.                     pname[i] = word_append(tlist, plist);
  425.                 /* promote types */
  426.                     typefixhack(pname[i]);
  427.                     break;
  428.                 }
  429.             }
  430.             if (!tlistdone) {
  431.                 tlist = typelist(plist);
  432.                 tlistdone = 1;
  433.             }
  434.             word_free(plist);
  435.             plist = word_alloc("");
  436.         }
  437. /* handle the end of a list */
  438.         else if (*buf == ';') {
  439.             if (!sawsomething)
  440.                 return ABORTED;
  441.             for (i = 0; i < np; i++) {
  442.                 if (!typed[i] && foundin(plist, pname[i])) {
  443.                     typed[i] = 1;
  444.                     word_free(pname[i]);
  445.                     pname[i] = word_append(tlist, plist);
  446.                     typefixhack(pname[i]);
  447.                     break;
  448.                 }
  449.             }
  450.             tlistdone = 0;
  451.             word_free(tlist); word_free(plist);
  452.             tlist = word_alloc("");
  453.             plist = word_alloc("");
  454.         }
  455. /* handle the  beginning of the function */
  456.         else if (!strcmp(buf, "{}")) break;
  457. /* otherwise, throw the word into the list (except for "register") */
  458.         else if (strcmp(buf, "register")) {
  459.             sawsomething = 1;
  460.             addword(plist, buf);
  461.             if (*buf == '(') inparen++;
  462.             if (*buf == ')') inparen--;
  463.         }
  464.     }
  465.  
  466. /* Now take the info we have and build a prototype list */
  467.  
  468. /* empty parameter list means "void" */
  469.     if (np == 0)
  470.         return word_alloc("void");
  471.  
  472.     plist = tlist = word_alloc("");
  473.     for (i = 0; i < np; i++) {
  474.  
  475. /* If no type provided, make it an "int" */
  476.         if ( !(pname[i]->next) ||
  477.        (!(pname[i]->next->next)&&strcmp(pname[i]->next->string, "void"))) {
  478.             addword(tlist, "int");
  479.         }
  480.         while (tlist->next) tlist = tlist->next;
  481.         tlist->next = pname[i];
  482.         if (i < np - 1)
  483.             addword(tlist, ", ");
  484.     }
  485.     return plist;
  486. }
  487.  
  488. /*
  489.  * emit a function declaration. The attributes and name of the function
  490.  * are in wlist; the parameters are in plist.
  491.  */
  492. void emit(wlist, plist, startline)
  493.     Word *wlist, *plist;
  494.     long  startline;
  495. {
  496.     Word *w;
  497.     int count = 0;
  498.  
  499. DEBUG("emit called\n");
  500.     if (donum)
  501.         printf("/*%8ld */ ", startline);
  502.  
  503.     for (w = wlist; w; w = w->next) {
  504.         if (w->string[0])
  505.             count ++;
  506.     }
  507.  
  508.     if (count < 2)
  509.         printf("int ");
  510.  
  511.     for (w = wlist; w; w = w->next) {
  512.         printf("%s", w->string);
  513.         if (ISCSYM(w->string[0]))
  514.             printf(" ");
  515.     }
  516.     if (docond)
  517.         printf("P((");
  518.     else
  519.         printf("( ");
  520.     for (w = plist; w; w = w->next) {
  521.         printf("%s", w->string);
  522.         if (ISCSYM(w->string[0]))
  523.             printf(" ");
  524.     }
  525.     if (docond)
  526.         printf("));\n");
  527.     else
  528.         printf(");\n");
  529. }
  530.  
  531. /*
  532.  * get all the function declarations
  533.  */
  534.  
  535. void getdecl(f)
  536.     FILE *f;
  537. {
  538.     Word *plist, *wlist = NULL;
  539.     char buf[80];
  540.     int sawsomething;
  541.     long startline;        /* line where declaration started */
  542.     int oktoprint;
  543. again:
  544.     word_free(wlist);
  545.     wlist = word_alloc("");
  546.     sawsomething = 0;
  547.     oktoprint = 1;
  548.  
  549.     for(;;) {
  550. DEBUG("main getdecl loop\n");
  551.         if (getsym(buf,f) < 0) {
  552. DEBUG("EOF in getdecl loop\n");
  553.              return;
  554.         }
  555. /* try to guess when a declaration is not an external function definition */
  556.         if (!strcmp(buf, ",") || !strcmp(buf, "{}") ||
  557.             !strcmp(buf, "=") || !strcmp(buf, "typedef") ||
  558.             !strcmp(buf, "extern")) {
  559.             skipit(buf, f);
  560.             goto again;
  561.         }
  562.         if (!dostatic && !strcmp(buf, "static")) {
  563.             oktoprint = 0;
  564.         }
  565. /* for the benefit of compilers that allow "inline" declarations */
  566.         if (!strcmp(buf, "inline") && !sawsomething)
  567.             continue;
  568.         if (!strcmp(buf, ";")) goto again;
  569.  
  570. /* A left parenthesis *might* indicate a function definition */
  571.         if (!strcmp(buf, "(")) {
  572.             startline = linenum;
  573.             if (!sawsomething || !(plist = getparamlist(f))) {
  574.                 skipit(buf, f);
  575.                 goto again;
  576.             }
  577.             if (plist == ABORTED)
  578.                 goto again;
  579.  
  580. /* It seems to have been what we wanted */
  581.             if (oktoprint)
  582.                 emit(wlist, plist, startline);
  583.             word_free(plist);
  584.             goto again;
  585.         }
  586.         addword(wlist, buf);
  587.         sawsomething = 1;
  588.     }
  589. }
  590.  
  591. void
  592. main(argc, argv)
  593. int argc; char **argv;
  594. {
  595.     FILE *f;
  596.     char *t, *iobuf;
  597.     extern void Usage();
  598.  
  599.     argv++; argc--;
  600.  
  601.     iobuf = malloc(NEWBUFSIZ);
  602.     while (*argv && **argv == '-') {
  603.         t = *argv++; --argc; t++;
  604.         while (*t) {
  605.             if (*t == 's')
  606.                 dostatic = 1;
  607.             else if (*t == 'n')
  608.                 donum = 1;
  609.             else if (*t == 'p')
  610.                 docond = 0;
  611.             else
  612.                 Usage();
  613.             t++;
  614.         }
  615.     }
  616.  
  617.     if (docond) {
  618.         printf("#ifdef __STDC__\n");
  619.         printf("# define\tP(s) s\n");
  620.         printf("#else\n");
  621.         printf("# define P(s) ()\n");
  622.         printf("#endif\n\n");
  623.     }
  624.     if (argc == 0)
  625.         getdecl(stdin);
  626.     else
  627.         while (argc > 0 && *argv) {
  628. DEBUG("trying a new file\n");
  629.             if (!(f = fopen(*argv, "r"))) {
  630.                 perror(*argv);
  631.                 exit(EXIT_FAILURE);
  632.             }
  633.             if (iobuf)
  634.                 setvbuf(f, iobuf, _IOFBF, NEWBUFSIZ);
  635.             if (dohead)
  636.                 printf("\n/* %s */\n", *argv);
  637.             linenum = 1;
  638.             newline_seen = 1;
  639.             glastc = ' ';
  640. DEBUG("calling getdecl\n");
  641.             getdecl(f);
  642. DEBUG("back from getdecl\n");
  643.             argc--; argv++;
  644.             fclose(f);
  645. DEBUG("back from fclose\n");
  646.         }
  647.     if (docond) {
  648.         printf("\n#undef P\n");    /* clean up namespace */
  649.     }
  650.     exit(EXIT_SUCCESS);
  651. }
  652.  
  653.  
  654. void Usage()
  655. {
  656.     fputs("Usage: mkproto [-n][-s][-p][files ...]\n",stderr);
  657.     fputs("   -n: put line numbers of declarations as comments\n",stderr);
  658.     fputs("   -s: include declarations for static functions\n", stderr);
  659.     fputs("   -p: don't make header files readable by non-ANSI compilers\n",
  660.           stderr);
  661.     exit(EXIT_FAILURE);
  662. }
  663.