home *** CD-ROM | disk | FTP | other *** search
/ Boston 2 / boston-2.iso / DOS / PROGRAM / C / GETF / BLDFUNCS.C next >
C/C++ Source or Header  |  1993-12-01  |  14KB  |  537 lines

  1. /*
  2.     bldfuncs - contruct a table of the functions defined in C
  3.                 source files.
  4.  
  5.     Usage: bldfuncs file1.c file2.c ...
  6.         (wildcards are allowed also, e.g. *.c )
  7.  
  8.     The output table is named funcs.txt
  9.  
  10.  
  11.     Copyright (c) 1988 Marvin Hymowech
  12.     Reference: DDJ, Aug 88
  13.  
  14. */
  15.  
  16. #include <stdio.h>
  17. #include <string.h>
  18.  
  19.  
  20. #define LINT_ARGS
  21. #define PRINTING_LINE_NUMBER
  22.  
  23. #define TRUE 1
  24. #define FALSE 0
  25. #define OK    0
  26. #define ERROR 1
  27.  
  28. /*
  29.     return values for filter functions below.
  30.     EOF or any character is also possible.
  31. */
  32. #define BRACES -2
  33. #define PARENS -3
  34. #define QUOTES -4
  35.  
  36. int line_num;        /* line number in input file */
  37.  
  38. /*    function prototypes for type checking */
  39. char *get_fn_name( char * );
  40. int get_names_one_file( char *, FILE * );
  41. int filter_data( FILE * );
  42. int filter_parens( FILE * );
  43. int filter_curly_braces( FILE * );
  44. int filter_ppdir( FILE * );
  45. int filter_quotes( FILE * );
  46. int filter_cmt( FILE * );
  47.  
  48. /* nonzero if new data is to be appended to existing file */
  49. int appending = 0;    
  50.  
  51. main(argc, argv) int argc; char **argv;
  52. {
  53.     FILE *fp_out;
  54.     char *current_file, **av, *cp, c;
  55.     int num_files, i, ac;
  56.  
  57.     quotearg(&argc, &argv);        /* expand wildcards on command line */
  58.  
  59.                 /*    parse option switches on command line & remove them */
  60.     ac = argc; argc = 1;
  61.     av = argv + 1;
  62.     while(--ac)
  63.         {if(**av == '-')
  64.             {cp = *av + 1;
  65.             while(c = *cp++)
  66.                 {switch(c)
  67.                     {case 'a': appending = 1; break;
  68.                     default: help();
  69.                     }
  70.                 }
  71.             }
  72.         else
  73.             argv[argc++] = *av;
  74.         av++;
  75.         }
  76.  
  77.     if(argc < 2) help();
  78.     if( (fp_out = fopen("funcs.txt", appending?"a":"w")) == NULL )
  79.         {
  80.         fprintf( stderr, "can't open %s\n", "funcs.txt" );
  81.         exit(1);
  82.         }
  83.     /*
  84.         build a function list for each file on the command line,
  85.         and write the list to the file funcs.txt.
  86.     */
  87.     printf( "%s funcs.txt...\n", appending?"Appending to":"Creating" );
  88.     num_files = argc - 1;
  89.     for ( i=1; i<=num_files; i++)
  90.         {                    /* tell the user where we're at */
  91.         printf( "%30s: %3d of %3d files\n",
  92.                                 current_file=strlwr(*++argv), i, num_files);
  93.         if( get_names_one_file( current_file, fp_out ) != OK )
  94.             {            /* use strlwr to lower_case the name - cosmetic only */
  95.             fprintf( stderr, "can't process %s", current_file );
  96.             exit(1);
  97.             }
  98.         }
  99.     fclose(fp_out);
  100.     exit(0);
  101. }
  102.  
  103. /*
  104.     open the .c file source_file_name, scan it for function definitions,
  105.     and write a listing to fp_out in the form:
  106.  
  107.         source_file_name:
  108.             function-1
  109.             function-2
  110.             ...
  111.             function-n;
  112.         return values: OK or ERROR
  113. */
  114. int
  115. get_names_one_file( source_file_name, fp_out )
  116. char *source_file_name;
  117. FILE *fp_out;
  118. {
  119.     int line_len, c, got_fn_defn=FALSE, definition_line_num;
  120. #define LINE_LEN 8192
  121.     char line[LINE_LEN], *name_ptr, fn_name[64];
  122.     FILE *fp_source;
  123.                             /* open the input source file */
  124.     if( (fp_source = fopen(source_file_name, "r")) == NULL )
  125.         return ERROR;
  126.  
  127.                             /* write "source file name:" */
  128.     sprintf( line, "\n%s:", source_file_name );
  129.     fprintf( fp_out, line );
  130.  
  131.     line_num=1;
  132.  
  133.     while( TRUE )
  134.         {
  135.         line_len = 0;            /* using the filter_data() char stream         */
  136.                                 /* collect chars until a semicolon or PARENS */
  137.         while( (c = filter_data(fp_source)) != EOF && c != ';' && c != PARENS )
  138.             line[line_len++] = c;
  139.         if( c == EOF )
  140.             break;
  141.         if( c == ';' )            /* bypass externals representing data items */
  142.             continue;            /* now we know line ended with PARENS         */
  143.         line[ line_len ] = 0;    /* terminate line */
  144.  
  145.         /*
  146.             At this point, line either contains a function definition
  147.             or a function type declaration or something else, perhaps
  148.             an occurrence of parentheses in a data item definition.  We
  149.             only want function definitions.
  150.         */
  151.  
  152.                         /* extract the function name from this possible        */
  153.                         /* function definition, and save it in fn_name.     */        
  154.         strcpy( fn_name, get_fn_name( line ) );
  155.         definition_line_num = line_num;
  156.  
  157.                         /* exclude the case of parenthetical expressions    */
  158.                         /* in a data definition, e.g. within array brackets */
  159.         if( !fn_name[0] )
  160.             continue;
  161.  
  162.                         /* skip white space */
  163.         while( (c = filter_data(fp_source)) != EOF && isspace(c) )
  164.             ; /* EMPTY */
  165.  
  166.         if( c == ';' || c == ',' )    /* functions type check declaration,    */
  167.             continue;                /*    so bypass it                        */
  168.  
  169.         if( c == PARENS )        /* something pointing to a function            */
  170.             continue;            /* bypass it (could be a mistake - if         */
  171.                                 /* it's a function returning a pointer         */
  172.                                 /* to a function we'll miss it)               */
  173.  
  174.         if( c == EOF )
  175.             break;
  176.  
  177.         
  178.                                 /* skip any parameter declarations -         */
  179.                                 /*    eat until braces or EOF                  */
  180.         while( c != BRACES && c != EOF )
  181.             c = filter_data(fp_source);
  182.  
  183.                                 /* append this function definition           */
  184.                                 /*    to the output table                      */
  185. #ifdef PRINTING_LINE_NUMBER
  186.         fprintf( fp_out, "\n\t%-25s", fn_name );
  187.         fprintf( fp_out, "%4d", definition_line_num );
  188. #else
  189.         fprintf( fp_out, "\n\t%s", fn_name );
  190. #endif
  191.         got_fn_defn = TRUE;
  192.     }
  193.     fclose(fp_source);
  194.                                 /* got_fn_defn FALSE if no functions in file */
  195.                                 /* write file terminator                     */
  196.     fputs( got_fn_defn ? ";\n" : "\n\t;\n", fp_out );
  197.     return OK;
  198. }
  199.  
  200. /*
  201.     assuming that the input line ends with a function name,
  202.     extract and return this name (as a pointer into the input line).
  203. */
  204. char *
  205. get_fn_name(line)
  206. char *line;
  207. {
  208.     char *name_ptr;
  209.     int len;
  210.  
  211.     if( !(len = strlen(line)) )
  212.         return line;
  213.  
  214.     name_ptr = line + len - 1;
  215.  
  216.     while( isspace(*name_ptr) )        /* skip trailing white space */
  217.         name_ptr--;
  218.     *(name_ptr + 1) = 0;            /* terminate fn name */
  219.  
  220.                                 /* function names consist entirely of        */
  221.                                 /* alphanumeric chars and underscores         */
  222.     while( (isalnum(*name_ptr) || *name_ptr == '_') && name_ptr >= line )
  223.         name_ptr--;
  224.                                 /* if this alleged function name begins        */
  225.                                 /* with a digit, return an empty string     */
  226.     if( isdigit(*++name_ptr) )
  227.         return( name_ptr + strlen(name_ptr) );
  228.     return name_ptr;            /* else return the function name             */
  229. }
  230.  
  231. /*
  232.     Using the stream returned by filter_parens() as input, return a
  233.     character stream in which any data initialization expressions
  234.     between an equals sign and a semicolon have been replaced by a
  235.     single semicolon.  This will filter out anything involving
  236.     parentheses in a data initialization expression, which might
  237.     otherwise be mistaken for a function definition.
  238. */
  239. int filter_data(fp_source)
  240. FILE *fp_source;
  241. {
  242.     int c;
  243.  
  244.     if( (c = filter_parens(fp_source)) != '=' )
  245.         return c;
  246.     while( (c = filter_parens(fp_source)) != ';' && c != EOF )
  247.         ;
  248.     return c;
  249. }
  250.  
  251. /*
  252.     Using the stream returned by filter_curly_braces() as input, return
  253.     a character stream in which all characters between '(' and the
  254.     matching ')' have been replaced by the single special value PARENS
  255.     (any nested parentheses within this top level pair will also have
  256.     been eaten).  This will filter out anything within the parentheses
  257.     delimiting the arguments in a function definition.
  258. */
  259. int
  260. filter_parens(fp_source)
  261. FILE *fp_source;
  262. {
  263.     int paren_cnt, c;
  264.  
  265.     if( (c = filter_curly_braces(fp_source)) != '(' )
  266.         return c;
  267.     paren_cnt = 1;
  268.     while( paren_cnt )
  269.         {switch( filter_curly_braces(fp_source) )
  270.             {
  271.             case ')':
  272.                 paren_cnt--;
  273.                 break;
  274.             case '(':
  275.                 paren_cnt++;
  276.                 break;
  277.             case EOF:
  278.                 return EOF;
  279.             }
  280.         }
  281.     return PARENS;
  282. }
  283.  
  284. /*
  285.     Using the stream returned by filter_ppdir() as input, return a
  286.     character stream in which all characters between '{' and the
  287.     matching '}' have been replaced by the single special value BRACES
  288.     (any nested braces within this top level pair will also have been
  289.     eaten).  This will filter out anything internal to a function.
  290. */
  291. int
  292. filter_curly_braces(fp_source)
  293. FILE *fp_source;
  294. {
  295.     int brace_cnt, c;
  296.  
  297.     if( (c = filter_ppdir(fp_source)) != '{' )
  298.         return c;
  299.     brace_cnt = 1;
  300.     while( brace_cnt )            /* wait for brace count to return to zero */
  301.         {switch( filter_ppdir(fp_source) )
  302.             {
  303.             case '}':
  304.                 brace_cnt--;    /* subtract right braces */
  305.                 break;
  306.             case '{':
  307.                 brace_cnt++;    /* add left braces */
  308.                 break;
  309.             case EOF:
  310.                 return EOF;
  311.             }
  312.         }
  313.     return BRACES;                /* brace count is now zero */
  314. }
  315.  
  316. #define MAXLINE 1024
  317.  
  318. /*
  319.     Using the stream returned by filter_quotes() as input, return a
  320.     character stream in which all preprocessor directives have been
  321.     eaten.
  322. */
  323. int
  324. filter_ppdir(fp_source)
  325. FILE *fp_source;
  326. {
  327.     int c, i;
  328.     char line[MAXLINE + 1];
  329.  
  330.     while(TRUE)
  331.         {                /* does this line begin a preprocessor directive? */
  332.         if( (c = filter_quotes(fp_source)) != '#' )
  333.             return c;    /* no, return character */
  334.                         /* yes, store until newline or EOF */
  335.         if( (c = get_ppdir_line( fp_source, line )) == EOF )
  336.             return EOF;
  337.         if( strncmp( line, "#define", 6) )        /* if not #define directive */
  338.             continue;
  339.         if( line[ strlen(line) - 1 ] != '\\' )    /* if #define ends with "\" */
  340.             continue;
  341.         else
  342.             while(TRUE)                            /* keep eating lines */
  343.                 {
  344.                 if( (c = get_ppdir_line( fp_source, line )) == EOF )
  345.                     return EOF;
  346.                             /* done with this #define directive if this
  347.                                 line is not also a continueation line */
  348.                 if( line[ strlen(line) - 1 ] != '\\' )
  349.                     break;
  350.                 }
  351.         }
  352. }
  353.  
  354. /*
  355.     Utility routine used by filter_ppdir() -
  356.     read the character stream using filter_quotes, storing characters
  357.     int he parameter "line", until EOF or '\n' is encountered.  Return
  358.     EOF or '\n' accordingly.
  359. */
  360. int
  361. get_ppdir_line( fp_source, line )
  362. FILE *fp_source;
  363. char *line;
  364. {
  365.     int i, c;
  366.  
  367.                                 /* store until newline or EOF */
  368.     for( i=0; i<MAXLINE && (c = filter_quotes(fp_source)) != '\n'
  369.                     && c != EOF; i++)
  370.         line[i] = c;
  371.     line[i] = 0;                /* terminate string */
  372.     if( c == EOF )
  373.         return EOF;
  374.     return '\n';
  375. }
  376.  
  377. /*
  378.     Using the stream returned by filter_cmt() as input, return a
  379.     character stream in which any quoted character or quoted string has
  380.     been collapsed to the single special value QUOTES to avoid
  381.     considering special characters like '{', '}', '(', or ')' which may
  382.     occur within quotes.  
  383. */
  384. int
  385. filter_quotes(fp_source)
  386. FILE *fp_source;
  387. {
  388.     int c1, c2;
  389.  
  390.     if( (c1 = filter_cmt(fp_source)) != '\'' && c1 != '"' )
  391.         return c1;        /* pass char through if not a single or double quote */
  392.     while( TRUE )
  393.         {
  394.         switch( c2 = filter_cmt(fp_source) )
  395.             {
  396.             case '\\':                    /* beginning of escape sequence */
  397.                 filter_cmt(fp_source);    /* so eat next char */
  398.                 break;
  399.             case EOF:
  400.                 return EOF;
  401.             default:
  402.                 if( c2 == c1 )            /* found end of quoted char or string */
  403.                     return QUOTES;
  404.             }
  405.         }
  406. }
  407.  
  408. /*
  409.     return character stream, eating comments.
  410.     Returns EOF if end of file.
  411.     nested comments are allowed.
  412. */
  413. int
  414. filter_cmt(fp_source)
  415. FILE *fp_source;
  416. {
  417.     /* values for state */
  418. #define STABLE        0        /* not in process of changing the comment
  419.                                 level: i.e., not in the middle of a
  420.                                 slash-star or star-slash combination */
  421. #define IN_CMT_FS     1        /* got '/', looking for '*' */
  422. #define OUT_CMT_STAR  2     /* got '*', looking for '/' */
  423.  
  424.     int c, state=STABLE, cmt_level = 0;
  425.  
  426.     while( TRUE )
  427.         {
  428.         c = fgetc(fp_source);
  429.  
  430.         if( c == '\n') line_num++;
  431.  
  432.         else if( c == EOF )
  433.             return EOF;
  434.  
  435.         switch(state)
  436.             {
  437.             case STABLE:
  438.                 if( c == '*' )
  439.                     state = OUT_CMT_STAR;
  440.                 else if( c == '/' )
  441.                     state = IN_CMT_FS;
  442.                 break;
  443.  
  444.             case IN_CMT_FS:
  445.                 if( c == '*' )
  446.                     {
  447.                     state = STABLE;
  448. #ifdef NESTED_COMMENTS
  449.                     cmt_level++;        /* descend one comment level */
  450. #else
  451.                     cmt_level = 1;        /* descend to comment level */
  452. #endif
  453.                     continue;
  454.                     }
  455.                 else if( !cmt_level )    /* if '/' not followed by '*'
  456.                                             and outside any comment   */
  457.                     {ungetc( c, fp_source );    /* push back this char
  458.                                                     and return the '/' */
  459.                     return '/';
  460.                     }
  461.                 else if( c != '/' )        /* stay in state IN_CMT_FS     */
  462.                     state = STABLE;        /* if next char is '/' as well */
  463.                 break;
  464.             
  465.             case OUT_CMT_STAR:
  466.                 if( c == '/' )
  467.                     {
  468.                     cmt_level--;        /* ascend one comment level */
  469.                     state = STABLE;
  470.                     continue;
  471.                     }
  472.                 else if( ! cmt_level )    /* if '*' not followed by '/' */
  473.                     {                    /* and outside any comment    */
  474.                     ungetc( c, fp_source );  /* push back this char   */
  475.                     return '*';                    /* and return the '*' */
  476.                     }
  477.                 else if( c != '*' )            /* stay in state IN_CMT_FS  */
  478.                     state = STABLE;            /* if next char is '*' as well */
  479.                 break;
  480.             }
  481.  
  482.         if( state == STABLE && !cmt_level )    /* if outside any comment */
  483.             return c;                        /* return character       */
  484.         }
  485. }
  486.  
  487.  
  488. /*
  489.  
  490. ------------------------  debugging code  --------------------------------
  491. #define RR(x) if(debugging) printf x        
  492. int debugging=1;
  493. #define filter_data(fp) copy(filter_data(fp))
  494. #undef filter_data
  495. FILE *dfile; 
  496.     dfile=fopen( "bld.out", "w" );
  497.     fclose(dfile);
  498.  
  499. char *copy(s) char *s;
  500. {    extern FILE *dfile;
  501.     fputs(s, dfile);
  502.     fputc('\n', dfile);
  503.     return s;
  504. }
  505.  
  506. int copy(c) int c;
  507. {    extern FILE *dfile;
  508.     if( c == EOF) fputs( "EOF", dfile );
  509.     else if( c == BRACES) fputs( "BRACES", dfile );
  510.     else if( c == PARENS) fputs( "PARENS", dfile );
  511.     else if( c == QUOTES) fputs( "QUOTES", dfile );
  512.     else fputc(c, dfile);
  513.     return c;
  514. }
  515.  
  516. int stdcopy(c) int c;
  517. {
  518.     if( c == EOF) fputs( "EOF", stdout );
  519.     else if( c == BRACES) fputs( "BRACES", stdout );
  520.     else if( c == PARENS) fputs( "PARENS", stdout );
  521.     else if( c == QUOTES) fputs( "QUOTES", stdout );
  522.     else if( c == '\n') fputs( "\\n", stdout );
  523.     else fputc(c, stdout);
  524.     return c;
  525. }
  526.  
  527. */
  528.  
  529. help()
  530. {
  531.     printf( "usage:   bldfuncs  [options]  foo.c  [bar.c ...]\n" );
  532.     printf( "   or:   bldfuncs  [options]  *.c\n" );
  533.     printf( "options...\n" );
  534.     printf( "    -a   append to output file rather than replacing it\n");
  535.     exit(1);
  536. }
  537.