home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / forut062.zip / ForUtil-0.62 / ffscan / ffscan.l < prev    next >
Text File  |  1996-08-28  |  17KB  |  657 lines

  1. %{
  2. #ifndef lint
  3. static char rcsId[]="$Source: /usr/local/rcs/ForUtil/ffscan/RCS/ffscan.l,v $";
  4. #endif
  5. /*****
  6. * fflow.l : f77 argument checker
  7. *
  8. * This file Version    $Revision: 1.5 $
  9. *
  10. * Creation date:    Wed Jul 17 22:38:31 GMT+0100 1996
  11. * Last modification:     $Date: 1996/08/28 17:50:18 $
  12. * By:            $Author: koen $
  13. * Current State:    $State: Exp $
  14. *
  15. * Author:        koen
  16. * (C)Copyright 1995-1996 Ripley Software Development
  17. * All Rights Reserved
  18. *
  19. * This program has one undocumented switch for debugging purposes:
  20. * -m for memory information under msdos
  21. *****/
  22. /*****
  23. * ChangeLog 
  24. * $Log: ffscan.l,v $
  25. * Revision 1.5  1996/08/28 17:50:18  koen
  26. * Changed verbose options and argument printing in count_args. 
  27. * Added print_version_id.
  28. *
  29. * Revision 1.4  1996/08/27 19:16:48  koen
  30. * Heavy changes: new scanner rules; optimized memory allocations; Moved
  31. * a number of routines to libflow.c
  32. *
  33. * Revision 1.3  1996/08/07 21:14:56  koen
  34. * Updated for MSDOS
  35. *
  36. * Revision 1.2  1996/08/02 14:50:47  koen
  37. * moved all system dependencies to lib/sysdeps.h
  38. *
  39. * Revision 1.1  1996/07/30 02:04:03  koen
  40. * Initial Revision. Based on fflow and scan_commons.
  41. *
  42. *****/
  43. #ifdef HAVE_CONFIG_H
  44. #include "autoconf.h"
  45. #endif
  46.  
  47. #include <stdio.h>
  48. #include <string.h>
  49. #include <ctype.h>
  50.  
  51. #include "forutil.h"
  52. #include "memmacros.h"
  53. #include "version.h"
  54. #include "libflow.h"
  55.  
  56. /**** Private Variables ****/
  57. static char *outfile;
  58. static int verbose, num_diff; 
  59. static int total_routines, total_calls, total_funcs;
  60. static int never_invoked;
  61. static FILE *output_file;
  62.  
  63. static char *usage = {"Usage: %s [-fhnqv] [-Eext] [-Idir] [-ofile] [-xname] [files]\n\n"
  64. " Scans the arguments of calls and compares them with the subroutine definition.\n\n"
  65. " Options\n"
  66. "\t-f: print precise location of filenames. Otherwise print name\n"
  67. "\t    of file only.\n"
  68. "\t-h, --help: print this help\n"
  69. "\t-n: tell what subroutines are never invoked\n"
  70. "\t-q: be really quiet (usefull if called from a script)\n"
  71. "\t-v: be verbose. Additional -v increases verbosiness\n"
  72. "\t--version: display version number\n"
  73. "\t-Eext: extensions to use for retrieving files. Multiple -E allowed.\n"
  74. "\t-Idir: get files from directory dir. Multiple -I allowed\n"
  75. "\t-ofile: file to write flowgraph to, default is stdout\n"
  76. "\t-xname.f: exclude file. Multiple -x allowed\n\n"
  77. "(C)Copyright 1995-1996 by Ripley Software Development\n"};
  78.  
  79. /**** Private Functions ****/
  80. static int set_global_option(char *arg);
  81. static inline void count_args(char *text);
  82. static void cross_check(FILE *file, flowInfo *list);
  83.  
  84. /*****
  85. * On the regular expressions below:
  86. * The routine and program regexps _must_ start with spaces (or tabs) (6 min.)
  87. * at the beginning of a line followed by the name of keyword subroutine or 
  88. * program and followed by a space (or a tab)
  89. * The regexp for call and function is different in that it does not have to 
  90. * start at the beginning of the line.
  91. * It must start with at least one space and may be preceeded by any combination
  92. * of whitespace, characters, digits and delimiters tokens.
  93. * The name of the (function,subroutine,program) may _not_ start with a left
  94. * or right brace or a single quote. The name itself may be made by any 
  95. * combination of alpha-numeric characters and underscores and can be followed
  96. * by an optional space or newline.
  97. * The regexps for the argument blocks is split in two parts.
  98. * The first part is the regexp for a single line of arguments: it may start 
  99. * with up to six spaces, followed by any possible character (continuation char)
  100. * any number of spaces and a left brace. After that any number of characters,
  101. * followed by an optional right brace, an optional space and and optional
  102. * newline.
  103. * The second part is an argument list that may start right after the name
  104. * of a (function,subroutine,program). It can have up to five spaces, any 
  105. * possible character except a space (continuation char), optional spaces,
  106. * an optional left brace. After that any number of chars followed by an
  107. * optional right brace, space and newline.
  108. * The regexp field <FNAME> contains three different regexps which will
  109. * match (to my knowledge) any formulation of a function or subroutine
  110. * definition with any sort of argument specification, including no arguments.
  111. *****/
  112. %}
  113.  
  114. routine  ^([ \t]{6,})subroutine[ \t]
  115. program  ^([ \t]{6,})program[ \t]
  116. function ^([ \t]{6,})[ [:alpha:]\*[:digit:]]*function[ \t]
  117. call     [^('][ \t]{1,}call[ ]
  118. name [^\(\)' ]*[a-zA-Z0-9_]+[ \n]?
  119. argline [ ]{0,6}.?[ ]*\(.+\)?[ ]?\n?
  120. argblock [ ]{5}[^ ][ ]*\(?.+\)?[ ]?\n?
  121. nl \n
  122.  
  123. %option caseless yylineno
  124.  
  125. %x FNAME
  126.  
  127. %%
  128. ^[*cC].* ; /* eat up comments entirely */  
  129. {program}    {
  130.             action = ADDFUNC; 
  131.             Type   = PROGRAM;
  132.             BEGIN FNAME;
  133.         }  
  134. {function}    {
  135.             numfunc++;
  136.             action = ADDFUNC; 
  137.             Type   = FUNCTION; 
  138.             BEGIN FNAME;
  139.         }  
  140. {routine}    {
  141.             numroutine++;
  142.             action = ADDFUNC; 
  143.             Type   = SUBROUTINE; 
  144.             BEGIN FNAME;
  145.         } 
  146. {call}        {
  147.             numcall++;
  148.             action = ADDCALL; 
  149.             Type   = UNKNOWN; 
  150.             BEGIN FNAME;
  151.         } 
  152. <FNAME>{name}{argline}* |
  153. <FNAME>{name}{argblock}* |
  154. <FNAME>{name}{argline}{argblock}* {
  155.             count_args(yytext);
  156.             BEGIN 0;
  157.         }  
  158. {nl}        BEGIN 0;
  159. .        ; /* do nothing  for all other remaining chars */
  160. %%
  161.  
  162. /*****
  163. * Count all arguments in the given text
  164. *****/
  165. static inline void
  166. count_args(char *text)
  167. {
  168.     char *string = text;
  169.     char func_name[64];
  170.     char *args = NULL;
  171.     int brace = 0, num_args = 0, num_line = 0, at_bol = 0;
  172.  
  173.     /* get function name */
  174.     for(;*string !='\0' && *string != '(' && *string != '\n' && 
  175.         !isspace(*string) ; string++);
  176.  
  177.     /* do not overflow name space */
  178.     strncpy(func_name, text, string - text > 64 ? 64 : string - text);
  179.  
  180.     /* null-terminate */
  181.     func_name[string-text] = '\0';
  182.     /* only if we have to be very verbose */
  183.     if(verbose > 2)
  184.     {    
  185.         char *chPtr;
  186.         int i = 0;
  187.         /* copy remaining args */
  188.         checked_alloca(args, strlen(text), char);
  189.         strncpy(args, string, strlen(string));
  190.         args[strlen(string)] = '\0';
  191.         printf("-------------------\n");
  192.         /* 
  193.         * print out the text preceeded with line numbers. 
  194.         * This slows things seriously down!
  195.         */
  196.         printf("line %i, text scanned:\n", yylineno);
  197.         printf("%i: ", i);
  198.         for(chPtr = text; *chPtr != '\0'; chPtr++)
  199.         {
  200.             putc(*chPtr, stdout);
  201.             if(*chPtr == '\n' && *(chPtr+1)!='\0')
  202.                 printf("%i:", ++i);
  203.         }
  204.         putc('\n', stdout);
  205.     }
  206.     while(*string)
  207.     {
  208.         switch(*string)
  209.         {
  210.             case '(':     /* initialize argcount. */
  211.                 if(num_args == 0)
  212.                     num_args++;
  213.                 brace++;
  214.                 break;
  215.             case ')':
  216.                 brace--;
  217.                 break;
  218.             case '*': /* eat up comments entirely */
  219.             case 'c':
  220.             case 'C':
  221.                 if(at_bol)
  222.                 {
  223.                     if(verbose > 2)
  224.                         printf("Rejecting comment on "
  225.                             "line %i\n", num_line); 
  226.                     while(*string != '\0' && 
  227.                         *string != '\n')
  228.                         string++;
  229.                     at_bol = 0;
  230.                 }
  231.                 break;
  232.             case '\'':
  233.                 string++;
  234.                 while(*string != '\0' && *string != '\'')
  235.                 {
  236.                     if(*string == '\n')
  237.                         num_line++;
  238.                     string++;
  239.                 }
  240.                 break;
  241.             case ',':     /* only when single left brace open*/
  242.                 if(brace == 1)    
  243.                     num_args++;
  244.                 break;
  245.         }
  246.         if(*string == '\0')
  247.             break;
  248.         at_bol = 0;
  249.         if(*string == '\n')
  250.         {
  251.             num_line++;
  252.             at_bol = 1;
  253.         }
  254.         string++;
  255.     }
  256.     /* only if we have to be very verbose */
  257.     if(verbose > 2)
  258.     {
  259.         if(action == ADDFUNC)
  260.             printf("routine name is %s\nargs are: %s", func_name,
  261.                 num_args == 0 ? "<none>\n":args);
  262.         if(action == ADDCALL)
  263.             printf("call made: %s\nargs supplied are: %s",
  264.                 func_name, num_args == 0 ? "<none>\n":args);
  265.         printf("brace count: %i\nargcount: %i\n", brace, num_args);
  266.         printf("-------------------\n");
  267.         fflush(stdout);
  268.     }
  269.     /* 19 continuation lines allowed by f77 standard */
  270.     if(num_line > 18)    
  271.     {
  272.             fprintf(stderr, "WARNING: more than 19 continuation "
  273.                 "lines ");
  274.             if(Type == ADDFUNC)
  275.                 fprintf(stderr, "in routine %s.\n", func_name); 
  276.             else
  277.                 fprintf(stderr, "in call to %s.\n", func_name);
  278.             fprintf(stderr,"near line %i in file %s%s\n", yylineno, 
  279.                 curr_path, curr_file);
  280.     }
  281.     if(brace != 0)
  282.     {
  283.         if(brace < 0)
  284.             fprintf(stderr, "WARNING: unbalanced right brace ");
  285.         else
  286.             fprintf(stderr, "WARNING: unbalanced left brace ");
  287.         fprintf(stderr, "on line %i of argument list ", num_line);
  288.         if(Type == ADDFUNC)
  289.             fprintf(stderr, "in routine %s.\n", func_name); 
  290.         else
  291.             fprintf(stderr, "in call to %s.\n", func_name);
  292.         fprintf(stderr,"near line %i in file %s%s\n", yylineno, 
  293.             curr_path, curr_file);
  294.     }
  295.     /*
  296.     * Depending on the action taken,
  297.     * add a new function to the flowgraph list or
  298.     * add an entry for a call made in the current 
  299.     * function
  300.     */
  301.     if(action == ADDFUNC)
  302.         add_new_func(func_name, yylineno, curr_path, curr_file, 
  303.             num_args);
  304.     else
  305.         add_new_call(func_name, yylineno, num_args);
  306. }
  307.  
  308. /*****
  309. * Perform argument check
  310. *****/
  311. static void
  312. cross_check(FILE *file, flowInfo *list)
  313. {
  314.     flowInfo *curr_func, *call; 
  315.     for(curr_func = list; curr_func != NULL; curr_func = curr_func->next) 
  316.     {
  317.         for(call = curr_func->calls; call != NULL; call = call->next)
  318.         {
  319.             if(call->parent && 
  320.                 call->num_args != call->parent->num_args)
  321.             {
  322.                 num_diff++;
  323.                 if(full_names)
  324.                 {
  325.                     fprintf(file, "---> %s called with %i "
  326.                         "args\n", 
  327.                         call->name, call->num_args);
  328.                     fprintf(file, "near line %i of %s%s\n",
  329.                         call->defline, curr_func->path,
  330.                         curr_func->file);
  331.                     fprintf(file, "Defined with %i args "
  332.                         "near line %i in \n",
  333.                         call->parent->num_args, 
  334.                         call->parent->defline);
  335.                     fprintf(file, "%s%s\n", 
  336.                         call->parent->path, 
  337.                         call->parent->file);
  338.                 }
  339.                 else
  340.                 {
  341.                     fprintf(file, "---> %s called with %i "
  342.                         "args ", 
  343.                         call->name, call->num_args);
  344.                     fprintf(file, "near line %i of %s\n", 
  345.                         call->defline, 
  346.                         curr_func->file);
  347.                     fprintf(file, "Defined with %i args ", 
  348.                         call->parent->num_args);
  349.                     fprintf(file, "near line %i in %s\n", 
  350.                         call->parent->defline, 
  351.                         call->parent->file);
  352.                 }
  353.             }
  354.         }
  355.     }
  356. }
  357.  
  358. /*****
  359. * Scan command line options
  360. *****/
  361. static int 
  362. set_global_option(char *arg)
  363.     switch(arg[0])
  364.     {
  365.         case 'f' :    /* use full names instead of filename only.*/
  366.             full_names = 1 ;
  367.             break;
  368.         case 'h' :    /* help requested */
  369.             printf(usage, progname); 
  370.             exit(2); 
  371.         case 'n' :    /* tell what subroutines are never invoked */
  372.             never_invoked = 1;
  373.             break;
  374. #ifdef __MSDOS__
  375.         case 'm' :    /* show memory usage under msdos */
  376.             need_mem_info_for_msdos = 1;
  377.             break;
  378. #endif
  379.         case 'q' :     /* be really quiet */
  380.             quiet = 1; 
  381.             verbose = 0; 
  382.             break; 
  383.         case 'v' :     /* be verbose */
  384.             verbose++;
  385.             break; 
  386.         case 'E' :    /* an extension specification */
  387.             SCAN_ARG("-E");
  388.             if(num_extensions == MAXEXTS)
  389.             {
  390.                 fprintf(stderr, "Capacity exceeded, more than "
  391.                     "%i extensions given on command line\n",
  392.                     MAXEXTS);
  393.                 fprintf(stderr,"Ignoring extension %s\n",arg+1);
  394.             }
  395.             else
  396.             {
  397.                 checked_malloc(ext_table[num_extensions], 
  398.                     strlen(arg+1)+1, char);
  399.                 strcpy(ext_table[num_extensions++], arg+1);
  400. #ifdef __MSDOS__ /* dos returns uppercase when building a filelist */
  401.                 upcase(ext_table[num_extensions-1]);
  402. #endif
  403.             }
  404.             return(1); 
  405.         case 'I' :    /* a directory specification */
  406.             SCAN_ARG("-I");
  407.             if(num_dirs_to_visit == MAXDIRS)
  408.             {
  409.                 fprintf(stderr,"Capacity exceeded, more than %i"
  410.                     " directories given on command line\n",
  411.                     MAXDIRS);
  412.                 fprintf(stderr,"Ignoring directory %s\n",arg+1);
  413.             }
  414.             else
  415.             {
  416.                 checked_malloc(dirs_to_visit[num_dirs_to_visit],
  417.                     strlen(arg+1)+1, char);
  418.                 strcpy(dirs_to_visit[num_dirs_to_visit++], 
  419.                     arg+1); 
  420.             }
  421.             return(1); 
  422.         case 'o' :    /* name of the output file */
  423.             SCAN_ARG("-o");
  424.             outfile = arg + 1;
  425.             return(1); 
  426.         case 'x' :    /* ignore the given file */
  427.             SCAN_ARG("-x");
  428.             if(num_ignore == MAXIGNORE)
  429.             {
  430.                 fprintf(stderr, "Capacity exceeded, more than "
  431.                     "%i ignores given on command line\n",
  432.                     MAXIGNORE);
  433.                 fprintf(stderr, "Ignoring option -x%s\n",arg+1);
  434.             }
  435.             else
  436.             {
  437.                 checked_malloc(ignore_list[num_ignore], 
  438.                     strlen(arg+1)+1, char);
  439.                 strcpy(ignore_list[num_ignore++], arg+1);
  440. #ifdef __MSDOS__ /* dos returns uppercase when building a filelist */
  441.                 upcase(ignore_list[num_ignore-1]);
  442. #endif
  443.             }
  444.             return(1); 
  445.         case '-' :            /* secondary options */
  446.             if(!strcmp("help", arg+1))    /* help wanted */
  447.             {
  448.                 printf(usage, progname); 
  449.                 exit(2); 
  450.             }
  451.             if(!strcmp("version", arg+1))    /* show version number*/
  452.             {
  453.                 print_version_id(progname, VERSION, "$Revision: 1.5 $");
  454.                 exit(2);
  455.             }        /* fall through */
  456.         default:
  457.             fprintf(stderr,"unknown option -%s\n", arg);
  458.             exit(3);
  459.     }
  460.     return(0); 
  461. }
  462.  
  463. /*****
  464. * main for ffscan
  465. *****/
  466. int 
  467. main (int argc, char **argv)
  468.     int i, narg;
  469.     char *arg;
  470.  
  471.     /* set global default values */
  472.     initialise(argv[0]);
  473.  
  474.     if(argc == 1) 
  475.     {
  476.         fprintf(stderr, "%s: no files given\n",progname);
  477.         printf(usage, progname);
  478.         exit(4);
  479.     }
  480.  
  481.     /* Default values */
  482.     outfile = "stdout";
  483.  
  484.     /* scan for any command line options */
  485.     arg = argv[narg = 1];
  486.     while ( narg < argc && arg[0] == '-' )
  487.     {
  488.         if (arg[0] == '-')
  489.         {
  490.             int num_opts = strlen(arg) ;
  491.             for(i = 1 ; i < num_opts ; i++ )
  492.             if(set_global_option(++arg))
  493.                 break;
  494.         }
  495.         arg = argv[++narg];
  496.     }
  497.     /* if no directories nor any files are given, exit */
  498.     if(num_dirs_to_visit == 0 && narg == argc)
  499.     {
  500.         fprintf(stderr,"no files given.\n");
  501.         exit(4);
  502.     }
  503.  
  504.     /* install signal handlers */
  505.     install_sig_handlers();
  506.  
  507.     /* open the output file */
  508.     if(!strcmp(outfile, "stdout"))
  509.         output_file = stdout;
  510.     else if((output_file = fopen(outfile, "w")) == NULL)
  511.     {
  512.         perror(outfile);
  513.         exit(6);
  514.     }
  515.  
  516.     /*
  517.     * always use this extension of none is given and directories are to be
  518.     * searched. 3 equals the size of .f\0
  519.     */
  520.     if(num_dirs_to_visit && !num_extensions)
  521.     {
  522.         checked_malloc(ext_table[num_extensions], 3, char);
  523. #ifdef __MSDOS__    /* msdos returns uppercase when building a filelist */
  524.         ext_table[num_extensions++] = ".F";
  525. #else
  526.         ext_table[num_extensions++] = ".f";
  527. #endif
  528.     }
  529.  
  530.     /* when directories have been given on the cmdline, build a filelist */
  531.     if(num_dirs_to_visit && verbose > 1)
  532.         printf("Scanning directories:\n");
  533.  
  534.     /* build a list of all files in the given directories (if any) */
  535.     for(i = 0 ; i < num_dirs_to_visit; i++)
  536.     {
  537.         if(verbose > 1)
  538.             printf("%s\n", dirs_to_visit[i]); 
  539.         build_file_list(&file_list, &num_files, dirs_to_visit[i],
  540.             ext_table, num_extensions); 
  541.     }
  542.     /* see if we have files left on the command line */
  543.     if(narg != argc)
  544.     {
  545.         for(i = narg ; i < argc ; i++)
  546.         {
  547.             checked_realloc(file_list, num_files+1, char*);
  548.             checked_malloc(file_list[num_files], strlen(argv[i])+1,
  549.                 char);
  550.             sprintf(file_list[num_files++], "%s", argv[i]);
  551.         }
  552.         if(verbose > 1 && argc-narg !=0)
  553.             printf("%i files given on command line.\n", argc - narg);
  554.     }
  555.  
  556.     /* show settings if we have to be a little bit verbose */
  557.     if(verbose > 1)
  558.         print_settings();
  559.  
  560.     if(verbose > 1)
  561.     {
  562.         printf("Total Files to scan: %i\n", num_files);
  563.         printf("File to write to: %s\n", outfile);
  564.     }
  565.     fflush(stdout);
  566.  
  567.     /* purge any non-matching output from yylex to /dev/null */
  568.     yyout = fopen(YYOUT_DEVICE, "w+");
  569.  
  570.     /* scan the file list */
  571.     for(i = 0; i < num_files; i++)
  572.     {
  573.         if ((num_ignore == 0 ? 1 : ignore_this_file(file_list[i])))
  574.         {
  575.             /* reset scanner for this file */
  576.             if((yyin = reset_scanner(file_list[i])) == NULL)
  577.                 continue;
  578.             yylineno = 0;
  579.             /* scan the file */
  580.             yylex();
  581.             /*
  582.             * flush the buffer of flex so we won't have
  583.             * any trailing stuff from the previous file
  584.             */
  585.             YY_FLUSH_BUFFER;
  586.             /* close input file */
  587.             fclose(yyin);
  588.             /* report some statistics if we have to */
  589.             if(verbose)
  590.                 print_file_stats(output_file, file_list[i], 
  591.                     yylineno);
  592.             total_routines += numroutine;
  593.             total_calls += numcall;
  594.             total_funcs += numfunc;
  595.         }
  596.     }
  597.     fclose(yyout);
  598.  
  599.     /* this is the final flowgraph data */
  600.     flow = global_flow.head;
  601.  
  602.     if(!quiet) 
  603.         printf("Sorting...\n"); 
  604.     fflush(stdout);
  605.  
  606.     /* Sort the flowgraph */
  607.     flow = sortAll(flow); 
  608.  
  609.     /* do argument check */
  610.     cross_check(output_file, flow);
  611.  
  612.     /* print subroutines that are never invoked */
  613.     if(never_invoked)
  614.         print_unused_routines(output_file, flow);
  615.  
  616.     if(verbose)
  617.     {
  618.         fprintf(output_file, "Checked %i files.\n", num_files);
  619.         fprintf(output_file, "Found %i subroutine definitions\n",
  620.             total_routines);
  621.         fprintf(output_file, "Found %i function definitions\n", 
  622.             total_funcs);
  623.         fprintf(output_file, "Found %i calls to subroutines\n", 
  624.             total_calls);
  625.         fprintf(output_file, "Checked %i calls in %i defined "
  626.             "subroutines in %i files.\n", 
  627.             total_calls, total_routines, num_files);
  628.     }
  629.  
  630.     /* report final results */
  631.     if(strcmp(outfile, "stdout") || !quiet)
  632.     {
  633.         if(num_diff)
  634.             fprintf(output_file, "Found %i argument mismatches.\n", 
  635.                 num_diff);
  636.         else
  637.             fprintf(output_file, "No argument mismatches found.\n");
  638.     }
  639.  
  640.     /* close output file */
  641.     if(strcmp(outfile, "stdout"))
  642.     {
  643.         fflush(output_file);
  644.         fclose(output_file);
  645.     }
  646.  
  647.     /* when wanted, print msdos memory usage summary */
  648.     print_dos_memory_usage();
  649.  
  650.     if(num_diff)
  651.         return(12);
  652.     else
  653.         return(0);
  654. }
  655.