/*************************************************************************/
/* */
/* flow.c - A program to analyse Fortran77 programs */
/* */
/* --------------------------------------------------------------------- */
/* Copyright (c) 1996 by Dirk Geschke, <geschke@physik.uni-kassel.de> */
/*************************************************************************/
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/*************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define VERSION 0.12
#define NO_SUB 1000
#define NO_PROG 1
#define NO_CALL 5000
/* declaration of functions */
void RoutineCheck(char *Line);
void Upcase(char *Line);
void Routine_name(char *Line,char *R_Name);
void help(char *PROG);
void Output_Result();
void SUB_find(char *Name);
/* new type declaration*/
struct Fort_Element
{
char Name [60];
int Call;
};
/* G L O B A L variables */
struct Fort_Element Subroutine[NO_SUB+1]; /* storages subroutine names */
struct Fort_Element Program; /* storages program name */
struct Fort_Element Call[NO_CALL]; /* storages CALL names */
int s_call=0; /* counts CALL statements */
int subrout=0; /* counts SUBROUTINE statements */
int if_count=0; /* counts IF statements */
long int Comment=0; /* counts COMMENT lines */
int Prog_Anf=-1; /* storages number of first CALL of SUBROUTINE from PROGRAM */
int Prog_Ende=0; /* storages number of last CALL of SUBROUTINE from PROGRAM */
int Level=1;
long int ActLine=0;
char Dat_Name [81]; /* name of actual file */
FILE *CallFile=NULL;
FILE *SubFile=NULL;
FILE *OUTFILE=NULL;
char S_OUT[]="FLOW.SUBS"; /* file name where to print subroutines */
char C_OUT[]="FLOW.CALL"; /* file name where to print call's */
int No_Main=0;
/* Optionkeys */
int O_SUBROUTINE=0;
int O_CALL=0;
int O_InFile=0;
int O_FIND=0;
int O_FICALL=0;
int O_FISUB=0;
int O_OutFile=0;
int O_RECURS=0;
int O_WARN72=0;
int O_NEWSTART=0;
/* begin of main */
int main(int argc, char *argv[])
{
int k,d,i;
char Line [81];
char firstChar;
char Option;
FILE *InFile=NULL;
long int Count_Lines=0;
char PRG_Name[60];
OUTFILE=stdout;
for (k=0; k<NO_SUB+1; k++) Subroutine[k].Call=0;
for (k=0; k<NO_CALL; k++) Call[k].Call=0;
Program.Call=0;
strcpy(PRG_Name,*argv);
if (argc==1) help(PRG_Name);
for (d=1; d<argc; d++)
{
argv++;
strcpy(Dat_Name,*argv);
if (Dat_Name[0] =='-' )
{
for(k=1; k<strlen(Dat_Name); k++)
{
Option=Dat_Name[k];
switch(Option)
{
case 'R' : O_RECURS=1;
break;
case 'S' : O_SUBROUTINE=1;
break;
case 's' : if (O_FISUB==0) /* skip step if repeated option */
{
if ((SubFile=fopen(S_OUT,"w")) == NULL)
{
fprintf(stderr,"\nError: Can't open file %s!\n\n",
S_OUT);
exit(2);
}}
O_FISUB=1;
break;
case 'c' : if (O_FICALL==0 ) /* skip step if repeated option */
{
if ((CallFile=fopen(C_OUT,"w")) == NULL)
{
fprintf(stderr,"\nError: Can't open file %s!\n\n",
C_OUT);
exit(2);
}}
O_FICALL=1;
break;
case 'C' : O_CALL=1;
break;
case 'f' : O_OutFile=1;
break;
case 'F' : O_InFile=1;
break;
case 'h' : help(PRG_Name);
break;
case 'r' : if (k+1 == strlen(Dat_Name))
{
if (d < argc)
{
argv++; d++; /* Counter one step further */
/* New starting point */
strcpy(Subroutine[0].Name,*argv);
Upcase(Subroutine[0].Name);
printf("\nChanged starting point to SUBROUTINE %s\n",\
Subroutine[0].Name);
}
}
if (strlen(Subroutine[0].Name) == 0)
{
/* An Error occured */
printf("\nError with option -r");
printf("\nMissing Name for starting SUBROUTINE");
exit(2);
}
O_NEWSTART=1;
break;
case 'U' : O_FIND=1;
break;
case 'V' : printf("\n\n%s Version %1.2f\n",PRG_Name,VERSION);
printf("Author: Dirk Geschke\n\n");
exit(2);
case 'W' : O_WARN72=1;
break;
default: fprintf(stderr,"\n\nunallowed option: -%c \n",Option);
help(PRG_Name);
/* exit(2); */
}
}
continue;
}
if ((InFile=fopen(Dat_Name,"r")) == NULL)
{
fprintf(stderr,"\nError: Can't open file %s!\n\n",Dat_Name);
exit(2);
}
ActLine=0; /* counts lines in actual file */
if (O_InFile) printf("\n Actual file: %s \n",Dat_Name);
while (fgets(Line,81,InFile) != NULL)
{
ActLine++;
Count_Lines++;
Upcase(Line);
firstChar = Line[0];
if ((firstChar == 'C') || (firstChar=='*') || (strlen(Line)<2))
Comment++;
else
{
if (O_WARN72) /* Control the length of the actual line */
{
i=strlen(Line);
if (i>73)
{
while (Line[i-2]==32) i--;
/* only empty spaces at the end? */
/* -1 for EOL sign and -1 because */
/* of beginning at Line[0] => i-2 */
if (i>73)
fprintf(stderr,"Warning: Line %ld in file %s \
consists of %d characters!\n",ActLine,Dat_Name,i-1);
}
}
RoutineCheck(&Line[6]);
}
}
/* if there is no definition of a SUBROUTINE in the same file
after the PROGRAM routine, use EOF as end of PROGRAM statement */
if (Program.Call==1)
{
Program.Call=0; Prog_Ende=s_call-1;
}
fclose(InFile);
}
if (O_FISUB) fclose(SubFile);
if (O_FICALL) fclose(CallFile);
if (InFile==NULL) help(PRG_Name);
if (strlen(Program.Name)==0)
{
fprintf(stderr,"\n\nError: No starting point found!\n\n");
No_Main=1;
}
if (O_OutFile)
{
strcpy(Dat_Name,Program.Name);
strcat(Dat_Name,".FLOW");
if ((OUTFILE=fopen(Dat_Name,"w")) == NULL)
{
fprintf(stderr,"\nError: Can't open file %s!\n\n",Dat_Name);
exit(2);
}
}
if (O_NEWSTART==1) /* new starting point */
{
fprintf(OUTFILE,"\n\n Results of subroutine %s:\n",Program.Name);
fprintf(OUTFILE," =======================");
}
else
{
fprintf(OUTFILE,"\n\n Results of program %s:\n",Program.Name);
fprintf(OUTFILE," ====================");
}
for (k=0; k<strlen(Program.Name); k++) fprintf(OUTFILE,"=");
fprintf(OUTFILE,"\n\n ");
fprintf(OUTFILE,"Total number of lines : %7ld \n ", Count_Lines);
fprintf(OUTFILE,"Comment lines : %7ld \n ", Comment);
fprintf(OUTFILE,"Code lines : %7ld \n ", Count_Lines-Comment);
fprintf(OUTFILE,"IF statements : %7d \n ", if_count);
fprintf(OUTFILE,"SUBROUTINE statements : %7d \n ", subrout);
fprintf(OUTFILE,"CALL statements : %7d \n\n", s_call);
if (No_Main==0) Output_Result();
if (O_OutFile) fclose(OUTFILE);
exit(0);
}
/* END of main */
/***************************************************************
* *
* This function converts line to uppercase character *
* *
***************************************************************/
void Upcase(char *Line)
{
char *w=Line;
while (*w !='\0' )
*w++=toupper(*w); /* chance each character TO UPPER char's */
}
/*****************************************************************
* *
* Checks for SUBROUTINE, PROGRAM, CALL. *
* Counting of CALLS and declaration of procedures *
* *
****************************************************************/
void RoutineCheck(char *Line)
{
int i=0;
int j=0;
int Klammer_auf=0;
static int p=0;
char Sign [11];
char *ptr;
char *ROUTINE="SUBROUTINE ";
char *CALL="CALL ";
char *PROG="PROGRAM ";
char *THEN="THEN";
while (Line[i]==32) i++; /* removing of leading spaces */
ptr=&Line[i];
if (strlen(ptr)<2)
{
Comment++; /* empty line = comment */
return;
}
/* Test for SUBROUTINE */
strncpy(Sign,ptr,11); /* copying first 10 character of string */
Sign[11]='\0'; /* ptr in sign1. */
if (strcmp(ROUTINE,Sign)==0) /* compares two strings */
{
if (Program.Call!=0)
{
Program.Call=0;
Prog_Ende=s_call-1;
}
subrout++;
if (subrout==NO_SUB)
{
fprintf(stderr,"Too many SUBPROCESS statements!\n");
fprintf(stderr,"Increase parameter NO_SUB!\n");
exit(2);
}
/* removing of leading space and extracting the */
/* name of SUBROUTINE out of line */
strcpy(Line,&Line[i+11]);
/* finding name of subroutine */
Routine_name(Line,Subroutine[subrout].Name);
/* Check if the Subroutine is the starting point of the actual run */
if ((O_NEWSTART==1) && (strcmp(Subroutine[0].Name,Subroutine[subrout].Name)==0))
{
p++;
if (p>1)
{
fprintf(stderr,"\n\aToo many definitions of SUBROUTINE %s!\n",\
Subroutine[0].Name);
exit(2);
}
Program.Call=1;
strcpy(Program.Name,Subroutine[0].Name); /* starting point found */
}
else
{
if (O_SUBROUTINE)
printf(" %s \n",Subroutine[subrout].Name);
if (O_FISUB)
fprintf(SubFile,"%-20s %-20s line %6ld\n",
Subroutine[subrout].Name,Dat_Name,ActLine);
}
return;
}
/* PROGRAM? If starting point is a Subroutine skip this step */
if (O_NEWSTART==0)
{
strncpy(Sign,ptr,8);
Sign[8]='\0';
if (strcmp(PROG,Sign)==0)
{
p++;
strcpy(Line,&Line[i+8]);
if (p>1)
{
fprintf(stderr,"\n\aToo many definitions of PROGRAM!\n");
exit(2);
}
Routine_name(Line,Program.Name);
strcpy(Subroutine[0].Name,Program.Name);
Program.Call=1;
return;
}
}
/* IF line? If true then find out if there is a CALL in it. */
if ((Line[i]=='I')&& (Line[i+1]=='F'))
if ((Line[i+2]==' ')||(Line[i+2]=='(')) /* IF line found */
{
if_count++;
for (j=i+2; j<strlen(Line); j++)
{
if (Line[j]=='(') Klammer_auf++;
if ((Line[j]==')') && (Klammer_auf))
{
/* one bounded pair of parenthesis */
Klammer_auf--;
if (Klammer_auf==0) /* outer parenthesis balanced */
{
j++;
ptr=&Line[j];
break; /* 'IF (...)' removed */
}
}
} /* END for j */
}
if (j>0) /* IF line */
{
i=j;
ptr=&Line[i];
while (Line[i]==32) i++; /* removing leading spaces! */
ptr=&Line[i];
/* Check for 'IF (...) THEN' structure */
strncpy(Sign,ptr,4);
if(strcmp(THEN,Sign)==0)
{
i+=4;
while (Line[i]==32) i++; /* removing leading spaces! */
ptr=&Line[i];
}
}
/* Check for CALL statement */
strncpy(Sign,ptr,5);
Sign[5]='\0'; /* append end-of-string sign */
if (strcmp(CALL,Sign)==0)
{
s_call++;
if (s_call==NO_CALL)
{
fprintf(stderr,"Too many CALL statements\n");
fprintf(stderr,"Increase parameter NO_CALL!\n");
exit(2);
}
strcpy(Line,&Line[i+5]);
Routine_name(Line,Call[s_call-1].Name);
Call[s_call-1].Call=subrout;
if (Program.Call==1)
{
if (Call[s_call-1].Call==subrout) /* 1. Call from Program */
{
if (Prog_Anf==-1)
{
Prog_Anf=s_call-1;
if (s_call==0) Prog_Anf=s_call;
Call[s_call-1].Call=0;
}
}
}
if (O_CALL) printf("CALL %s \n",Call[s_call-1].Name);
if (O_FISUB)
fprintf(CallFile,"%-20s %-20s line %6ld\n",Call[s_call-1].Name,
Dat_Name,ActLine);
}
}
/**************************************************************
* *
* This function is used to extract the name of called *
* or defined procedure. *
* *
**************************************************************/
void Routine_name(char *Line, char *R_Name)
{
int i=0;
int offset=0;
/* removing of leading spaces */
while (Line[offset]==32) offset++;
/* space, opened parenthese or EOL are a sign for end of routine name */
while ((Line[i+offset] !=' ' ) && (Line[i+offset] != '(' )
&& (Line[i+offset] !='\n'))
{
R_Name[i]= Line[i+offset];
i++;
}
R_Name[i]='\0'; /* append end of string! */
}
/********************************************************************
* *
* Printing if error with input or option -h *
* *
********************************************************************/
void help(char *PROG)
{
printf("\n\nUsage: %s [-hCcFSsV] file [files... ] \n\n",PROG);
printf(" -h: Printing of this message \n");
printf(" -C: Printing of each found CALL during the input\n");
printf(" -c: Each found CALL statement is reported \n");
printf(" in a file named %s with the name of the\n", C_OUT);
printf(" file where it was found and the line number\n");
printf(" -F: Print name of file actually on work \n");
printf(" -f: The output is written to a file with the name \n");
printf(" of the <program>.FLOW\n");
printf(" -U: Printing of unknown SUBROUTINE calls\n");
printf(" -r <name>: Set a new starting point. Start with the \n");
printf(" SUBROUTINE <name> instead of PROGRAM line\n");
printf(" -R: Omit following recursive calls\n");
printf(" -S: Printing of each found SUBROUTINE during the input\n");
printf(" -s: Each found SUBROUTINE is reported in a file \n");
printf(" named %s with the name of the\n", S_OUT);
printf(" file where it was found and the line number\n");
printf(" -V: Number of version\n");
printf(" -W: A warning is given if a code line exceeds the\n");
printf(" length of 72 characters\n");
printf("\nFor further information see manual page flow(1).\n\n");
exit(2);
}
/********************************************************************
* *
* Printing of SUBROUTINE calls out of main PROGRAM *
* *
********************************************************************/
void Output_Result()
{
int i,k;
for (i=Prog_Anf; i<Prog_Ende+1; i++)
{
fprintf(OUTFILE,"%s \n",Call[i].Name); /* Level 0 call */
for (k=1; k<subrout+2; k++) Subroutine[k].Call=0;
Level=0;
SUB_find(Call[i].Name);
}
}
/*********************************************************************
* *
* Printing the name of the called SUBROUTINE and maybe *
* finding the called SUBROUTINE in the input and write *
* a warning if not found. *
*********************************************************************/
void SUB_find(char *Name)
{
int i,j,k;
int found=0;
for (i=1; i<subrout+2; i++) /* i=0 : PROGRAM statement! */
{
if (strcmp(Name,Subroutine[i].Name)==0)
{
found=1; /* definition of SUBROUTINE found */
for (j=0; j<s_call; j++)
{
if (Call[j].Call==i)
{
/* if (Subroutine[i].Call != Level)*/
{
Subroutine[i].Call=Level;
if (O_RECURS ) Call[j].Call=0;/* omit recursive hanging */
Level++;
/* printing subroutine call */
for (k=0; k<Level; k++) fprintf(OUTFILE," ");
fprintf(OUTFILE,"%s\n",Call[j].Name);
/* Further CALL from called subroutine? */
SUB_find(Call[j].Name);
Level--; /* reduce Level by one step */
}
}
}/* END for j */
}
}
/* Is the called SUBROUTINE declared in any input file? */
if ((found==0) && (O_FIND))
{
fprintf(stderr,"\n WARNING: Subroutine %s not found! \n",Name);
}
}
syntax highlighted by Code2HTML, v. 0.9.1