#!/usr/bin/perl
# ------------------------------------------------------------------------------
# NAME
#   Ecmwf::Fortran90_stuff
#
# DESCRIPTION
#   This is a module for analysing Fortran 9X code. It is used by the FCM
#   system to generate interface blocks for Fortran 9X free source files.
#
# ABOUT THIS MODULE:
#   The original version of this module was developed by the European Centre
#   for Medium-Range Weather Forecasts (ECMWF). This version has been modified
#   by UK Met Office to become part of the FCM system.
# ------------------------------------------------------------------------------

package Ecmwf::Fortran90_stuff;

# Standard pragmas
use strict;
use warnings;

# Standard modules
require Exporter;

our @ISA      = qw(Exporter);
our @EXPORT   = qw(study setup_parse pre_tidy remove_macro expcont 
                   process_include_files tidy tidy_decl getvars 
		   find_unused_vars remove_unused_vars doctor_viol
		   fix_doctor_viol various cont_lines f90_indent
		   writefile readfile create_interface_block
		   add_interface_blocks change_var_names insert_hook
		   remake_arg_decl remove_some_comments parse_prog_unit
		   get_calls_inc);

# ------------------------------------------------------------------------------

# Module variables

my $fname = '';

# ------------------------------------------------------------------------------
# SYNPOSIS
#   $file = &Ecmwf::Fortran90_stuff::fname ();
#   &Ecmwf::Fortran90_stuff::fname ($file);
#
# DESCRIPTION
#   This function returns the value in the module variable $fname, which is the
#   name of the input Fortran source file from FCM. If an argument exists, the
#   value of $fname is set to the value of the argument.
#
# ------------------------------------------------------------------------------

sub fname {
  $fname = $_[0] if @_;
  return $fname;
}

#==========================================================================
sub study{
# Study statements and put attributes into array $statements
# Attributes assigned:
# $href->{content}       - What statement it is
# $href->{decl}       - true if declaration,
#                       5 means statement function
#                       4 means USE statement,
#                       2 means type decleration
#                       3 means FORMAT statement
#                       1 means the rest
# $href->{in_contain} - true while in internal procedure(s)
# $href->{exec}       - true if executable statement
#                     - 2 means first executable statement in program unit
#                     - 3 means last executable statement in program unit
#                     - 23 means first and last executable statement in program unit
# $href->{prog_unit}  - program unit number (numbered from 0)
# $href->{number}     - statement number (numbered from 0)
 
# Further attributes will be assigned later (action attributes)

  my($statements,$prog_info) = @_;

  our ($name,$nest_par);
  my ($unit_name,@args,$prog_unit,$href,@punit,$current_punit);
  my ($content,$decl,$exec);
  my($type_def)=0;
  my($unit_count)=-1;
  @punit=();
  $current_punit='';
  my $number=-1;
  my $in_contain=0;
  my $in_interface=0;
  my $contain_host='';
  my $current_unit_name='';
  our($study_called);
#  if(! $study_called) {
#    $$prog_info{has_interface_block}=0;
#  }
# Initial "parsing" loop

  foreach $href (@$statements) {
    $href->{in_contain}=$in_contain;
    $href->{contain_host}=$contain_host if($in_contain);
    $number++;
    $_=$href->{statement};
    $content='unknown';
    my $content2='';
    $decl=0;
    $exec=0;

    if($type_def) {
 #     $href->{content}='typedef';
    }
    
# Comment
CRACK:    {
      if(/^\s*(?:!|$)/) {
	$content='comment';
	last CRACK;
      }

      $_ = uc unless /^#/;
      s/^\s*//;
      s/\!.*\n/\n/g; # Remove trailing comments in all lines
#      print "AA $_";
 
# Program name statement 
      if($content eq 'unknown' and ! $in_interface) {
	$prog_unit=&parse_prog_unit(\$unit_name,\@args);
	if($prog_unit) {
          $current_unit_name=$unit_name;
	  $content=uc($prog_unit);
	  push(@punit,$prog_unit);
	  $current_punit=$prog_unit;
	  $unit_count++;
	  if(! $study_called) {
	    $$prog_info{'unit_name'}[$unit_count]=uc($unit_name);
	    $$prog_info{'unit_name'}[$unit_count]=uc($unit_name);
#	  $$prog_info{'tokens'}[$unit_count]=[];
	    if($prog_unit eq 'module') {
	      $$prog_info{'is_module'}=1;
	      $$prog_info{'module_name'}=$unit_name;
	    }
	  }
	  last CRACK;
	}
      }
      if($content eq 'unknown') {
	$decl=0;
	$exec=1;
# Executable constructs
	&study_exec(\$content,$prog_info,\$study_called);
	if($content eq 'IF') {
	  s/^IF\s*$nest_par\s*//;
	  &study_exec(\$content2,$prog_info,\$study_called);
	}
      }
      

      if($content eq 'unknown') {
# Specification statemnts
	$exec=0;
	$decl=1;
	if(/^USE\b/) {
	  $content='USE';
	  $decl=4;
	}
	elsif(/^INTEGER\b/) {
	  $content='INTEGER';
	  $decl=2;
	}
	elsif(/^REAL\b/) {
	  $content='REAL';
	  $decl=2;
	}
	elsif(/^LOGICAL\b/) {
	  $content='LOGICAL';
	  $decl=2;
	}
	elsif(/^CHARACTER\b/) {
	  $content='CHARACTER';
	  $decl=2;
	}
	elsif(/^DOUBLE\s*PRECISION\b/) {
	  $content='DOUBLE PRECISION';
	  $decl=2;
	}
	elsif(/^COMPLEX\b/) {
	  $content='COMPLEX';
	  $decl=2;
	}
	elsif(/^TYPE *\(/) {
	  $content='type_decl';
	  $decl=2;
	}
	elsif(/^ALLOCATABLE\b/) {
	  $content='ALLOCATABLE';
	}
	elsif(/^COMMON\b/) {
	  $content='COMMON';
	}
	elsif(/^DATA\b/) {
	  $content='DATA';
	}
	elsif(/^DIMENSION\b/) {
	  $content='DIMENSION';
	}
	elsif(/^EQUIVALENCE\b/) {
	  $content='EQUIVALENCE';
	}
	elsif(/^EXTERNAL\b/) {
	  $content='EXTERNAL';
	}
	elsif(/^\d+\s+FORMAT\b/) {
	  $content='FORMAT';
	  $decl=3;
	}
	elsif(/^IMPLICIT\b\s+NONE\b/) {
	  $content='IMPLICIT NONE';
	}
	elsif(/^IMPLICIT\b/) {
	  $content='IMPLICIT';
	}
	elsif(/^INTENT\b/) {
	  $content='INTENT';
	}
	elsif(/^INTRINSIC\b/) {
	  $content='INTRINSIC';
	}
	elsif(/^NAMELIST\b/) {
	  $content='NAMELIST';
	}
	elsif(/^OPTIONAL\b/) {
	  $content='OPTIONAL';
	}
	elsif(/^PARAMETER\b/) {
	  $content='PARAMETER';
          $decl = 2;
	}
	elsif(/^POINTER\b/) {
	  $content='POINTER';
	}
	elsif(/^PUBLIC\b/) {
	  $content='PUBLIC';
	}
	elsif(/^PRIVATE\b/) {
	  $content='PRIVATE';
	}
	elsif(/^SAVE\b/) {
	  $content='SAVE';
	}
	elsif(/^TARGET\b/) {
	  $content='TARGET';
	}
	elsif(/^SEQUENCE\b/) {
	  $content='SEQUENCE';
	}
	elsif(/^INTERFACE\b/) {
	  $content='INTERFACE';
	  if(! $study_called) {
	    $$prog_info{has_interface_block}=1;
	    $in_interface=1;
	  }
	}
	elsif(/^END ?INTERFACE\b/) {
	  $content='END INTERFACE';
	    $in_interface=0;
	}
	elsif(/^TYPE *[^\( ]/i) {
	  $content='type_def';
	  $type_def=1;
	}
	elsif(/^END\s*TYPE\b/){
	  $content='type_def';
	  $type_def=0;
	}
        elsif( $in_interface ) {
          if(/^MODULE PROCEDURE\b/) {
            $content='MODULE PROCEDURE';
          }
        }
      }
# Other constructs
      if($content eq 'unknown') {
	$decl=0;
	$exec=0;
	
	if(/^CONTAINS\b/) {
	  $content='CONTAINS';
	  $in_contain=1;
          $contain_host=uc($current_unit_name);
	  if(! $study_called) {
	    $$prog_info{has_contain}=1;
	    $$prog_info{containing}=1;
	  }
	}
	elsif(/^(?:INCLUDE|#include)\b/) {
	  $content='include';
	  if(! $study_called) {
	    $$prog_info{has_include}=1;
	  }
	}
	elsif(/^\#/) {
	  $content='cpp';
	}
	elsif(/^\@/) {
	  $content='compiler_directive';
	}
	
	else{
	  if(/^END\b/ and ! $in_interface) {
	    $prog_unit=pop(@punit);
	    $content='END '.uc($prog_unit);
	    if($in_contain) {
	      unless(@punit) {
		$unit_count=0;
		$href->{in_contain}=0;
		$in_contain=0;
	      }
	    }
	  }
	} 
      }
    }
    
    if($in_interface and $content ne 'INTERFACE') {
      $content='in_interface';
      $exec=0;
      $decl=1;
    }

#    print "BB $unit_count $content $_";
    if($content  eq 'unknown') {
      print STDERR $fname, ': failed to crack statement starting at line ',
                   $href->{first_line}, ', - syntax error?', "\n";
      print STDERR ' ', $_, "\n";
#      print STDERR "study_called $study_called in_interface $in_interface \n";
#      print STDERR Dumper($statements);
      #die "Failed in study";
    }
#    unless($content eq 'comment') {
#      my @tmpvar=/\b$name\b/g;
#      my $i=0;
#      foreach my $tmp (@tmpvar){ 
#	$href->{'tokens'}[$i]=$tmp;
#	$i++;
#	if(! $study_called and $unit_count > -1) {
#	  $$prog_info{'token_hash'}[$unit_count]{$tmp}++;
#	}
#      }
#    }
               
    $href->{content}=$content;
    $href->{content2}=$content2 if($content2);
    $href->{decl}=$decl;
    $href->{exec}=$exec;
#    $href->{type_decl}=$type_decl;
    $href->{prog_unit}=$unit_count;
    $href->{number}=$number;
    unless($content eq 'comment') {
      $href->{multi_line} = 1 if(tr/\n// > 1);
    }
  }


# Find first executable statement in each program unit
# Also repair statement functions wrongly assigned as executable
  my $prev_unit_count=-2;
  my $stat_func_suspicion=0;
  my @lastexec=();

  foreach $href (@$statements) {
    $exec=$href->{exec};
    $unit_count=$href->{prog_unit};
    if($exec) {
      if($unit_count > $prev_unit_count) {
	$content=$href->{content};
	if($content eq 'array_assign') {
	  $stat_func_suspicion=1;
	  $_=$href->{statement};
	  if(/^\s*$name\s*\(\s*:/){
	    $stat_func_suspicion=0;
#	    print " A $_";
	   } 
	  elsif(/^\s*$name\s*\(\s*$name\s*:/){
	    $stat_func_suspicion=0;
#	    print " B $_";
	  } 
	  elsif(/^\s*$name\s*\(\s*\d+/){
	    $stat_func_suspicion=0;
#	    print " C $_";
	  }
	  else {
	    $href->{exec}=0;
	    $href->{decl}=5;
	    $href->{content}='statmf';
#	    print " D $_";
	    next;
	  }
	}
	$href->{exec}=2;
	$prev_unit_count=$unit_count;
	$content=$href->{content};
      }
      $lastexec[$unit_count]=$href->{number}  unless ($unit_count < 0);  
# No prog_unit assigned, include file?
    }
  }

# Assign last executable statement
  if(@lastexec) {
    foreach my $last (@lastexec) {
      if(defined ($last)) {
	if($$statements[$last]->{exec} == 1) {
	  $$statements[$last]->{exec}=3;
	}
	else{
	  $$statements[$last]->{exec}=23;
	}      
      }
    }
  }
# Consistency checks
  my $fail=0;
  my $prev_exec=0;
  $prev_unit_count=-1;
  foreach $href (@$statements) {
    $content=$href->{content};
    next if($content eq 'comment');
    $unit_count=$href->{prog_unit};
    $exec=$href->{exec};
    $decl=$href->{decl};
    if($unit_count == $prev_unit_count) {
      if($decl and $prev_exec) {
	unless ($content eq 'FORMAT' | $content eq 'DATA' ) {
          die $fname, ': declaration after executable statement', "\n",
              $href->{first_line}, ' ', $href->{statement}, "\n";
	}
      }
    }
    $prev_unit_count=$unit_count;
    $prev_exec=$exec;
  }

  $study_called=1;
}

#==========================================================================
sub study_exec{
  my($content,$prog_info,$study_called) = @_;
  our ($name,$nest_par);
  if(/^(\w+\s*:\s*)*IF\s*$nest_par\s*THEN/) {
    $$content='IF_construct';
  }
  elsif(/^ELSE\s*IF\s*\(/) {
    $$content='ELSEIF';
  }
  elsif(/^ELSE\b\s*($name)*/) {
    $$content='ELSE';
  }
  elsif(/^END\s*IF\b\s*($name)*/) {
    $$content='ENDIF';
  }
  elsif(/^(?:\d+\s+)?($name\s*:\s*)*DO(\s+WHILE)?\b/) {
    $$content='DO';
  }
  elsif(/^(?:\d+\s+)?END\s*DO\b/) {
    $$content='ENDDO';
  }
  elsif(/^(?:\d+\s+)?ALLOCATE\b/) {
    $$content='ALLOCATE';
  }
  elsif(/^ASSIGN\b/) {
    $$content='ASIGN';
  }
  elsif(/^(?:\d+\s+)?BACKSPACE\b/) {
    $$content='BACKSPACE';
  }
  elsif(/^(?:\d+\s+)?CALL\b/) {
    $$content='CALL';
    if(!$$study_called) {
      $$prog_info{no_calls}++;
    }
  }
  elsif(/^(?:\d+\s+)?CLOSE\b/) {
    $$content='CLOSE';
  }
  elsif(/^(?:\d+\s+)?CONTINUE\b/) {
    $$content='CONTINUE';
  }
  elsif(/^(?:\d+\s+)?CYCLE\b/) {
    $$content='CYCLE';
  }
  elsif(/^(?:\d+\s+)?DEALLOCATE\b/) {
    $$content='DEALLOCATE';
  }
  elsif(/^ENDFILE\b/) {
    $$content='ENDFILE';
  }
  elsif(/^(?:\d+\s+)?EXIT\b/) {
    $$content='EXIT';
  }
  elsif(/^(?:\d+\s+)?GO\s*TO\b/) {
    $$content='GOTO';
  }
  elsif(/^(?:\d+\s+)?IF\s*\(/) {
    $$content='IF';
  }
  elsif(/^(?:\d+\s+)?INQUIRE\b/) {
    $$content='INQUIRE';
  }
  elsif(/^(?:\d+\s+)?NULLIFY\b/) {
    $$content='NULLIFY';
  }
  elsif(/^(?:\d+\s+)?OPEN\b/) {
    $$content='OPEN';
  }
  elsif(/^(?:\d+\s+)?PAUSE\b/) {
    $$content='PAUSE';
  }
  elsif(/^(?:\d+\s+)?PRINT\b/) {
    $$content='PRINT';
  }
  elsif(/^(?:\d+\s+)?(?:READ|BUFFER\s*IN)\b/) {
    $$content='READ';
  }
  elsif(/^(?:\d+\s+)?RETURN\b/) {
    $$content='RETURN';
  }
  elsif(/^(?:\d+\s+)?REWIND\b/) {
    $$content='REWIND';
  }
  elsif(/^(?:\d+\s+)?STOP\b/) {
    $$content='STOP';
  }
  elsif(/^(?:\d+\s+)?(?:WRITE|BUFFER\s*OUT)\s*\(/) {
    $$content='WRITE';
  }
  elsif(/^(?:\d+\s+)?($name\s*:\s*)*SELECT\s*CASE\b/) {
    $$content='SELECT CASE';
  }
  elsif(/^(?:\d+\s+)?CASE\b/) {
    $$content='CASE';
  }
  elsif(/^(?:\d+\s+)?END\s*SELECT\b/) {
    $$content='END SELECT';
  }
  elsif(/^(?:\d+\s+)?WHERE\s*$nest_par\s*$name.*=/) {
    $$content='WHERE';
  }
  elsif(/^(?:\d+\s+)?WHERE\s*\(/) {
    $$content='WHERE_construct';
  }
  elsif(/^ELSE\s*WHERE\b/) {
    $$content='ELSEWHERE';
  }
  elsif(/^END\s*WHERE\b/) {
    $$content='ENDWHERE';
  }
  elsif(/^(?:\d+\s+)?FORALL\s*\(/) {
    $$content='FORALL';
  }
  elsif(/^END\s*FORALL\b/) {
    $$content='ENDFORALL';
  }
  elsif(/^(?:\d+\s+)?$name(?:\s*%\s*$name)*\s*=/o) {
    $$content='scal_assign';
  }
  elsif(/^(?:\d+\s+)?$name(?:\s*$nest_par)*(?:\s*%\s*$name(?:\s*$nest_par)?)*\s*=/o) {
    $$content='array_assign';
  }
}
#===================================================================================
sub pre_tidy {

# Initial tidying to make the rest work

  my($lines)=@_;
  foreach (@$lines) {

# Substitute tab with four blanks
    s/\t/    /g;
    s/^ *INTEGER /INTEGER_M /i;
    s/^ *REAL /REAL_B /i;
  }
}
#==========================================================================
sub remove_macro {

# Remove INTEGER_M, _ONE_ etc. macros and replace by expanded statement

  my($lines)=@_;

  my($im)=1; # Until I start checking include files
  my($ia)=0;
  my($ib)=0;
  my($rb)=1; # Until I start checking include files
  my($is)=0;
  my($rh)=0;
  my($rm)=0;
  my(@pars,$string);
  for (@$lines) {
    next if(/^ *$/ | /^ *!/);
# The following two substitutions should be restored at end of processing
    s/(\'[^!]*)!+(.*\')/$1\£$2/;   # Protect against mischief 
    s/(["][^!]*)!+(.*["])/$1\£$2/;      # Protect against mischief
    $im=$im+/JPIM\b/i unless($im);
    $rb=$rb+/JPRB\b/i unless($rb);
    $rm=$rm+/JPRM\b/i unless($rm);
    $im=$im+s/\bINTEGER_M\b/INTEGER(KIND=JPIM)/o;
    $ia=$ia+s/\bINTEGER_A\b/INTEGER(KIND=JPIA)/o;
    $ib=$ib+s/\bINTEGER_B\b/INTEGER(KIND=JPIB)/o;
    $is=$is+s/\bINTEGER_S\b/INTEGER(KIND=JPIS)/o;
    $rb=$rb+s/\bREAL_B\b/REAL(KIND=JPRB)/o;
    $rh=$rh+s/\bREAL_H\b/REAL(KIND=JPRH)/o;
    $rm=$rm+s/\bREAL_M\b/REAL(KIND=JPRM)/o;
    $rb=$rb+s/\b_ZERO_\b/0.0_JPRB/og;
    $rb=$rb+s/\b_ONE_\b/1.0_JPRB/og;
    $rb=$rb+s/\b_TWO_\b/2.0_JPRB/og;
    $rb=$rb+s/\b_HALF_\b/0.5_JPRB/og;
  }
  @pars=();
  push(@pars,"JPIM") if $im;
  push(@pars,"JPRB") if $rb;
  push(@pars,"JPRM") if $rm;
  push(@pars,"JPIA") if $ia;
  push(@pars,"JPIB") if $ib;
  push(@pars,"JPIS") if $is;
  ($string=join('     ,',@pars))=~s/ *$//;
  for (@$lines) {
    next unless (/^\#/);
    if(@pars) {
      s/^#include +"tsmbkind.h"/USE PARKIND1  ,ONLY : $string/ ;
    }
    else {
      s/^#include +"tsmbkind.h"//;
    }
#    if($rh) {
      s/^#include +"hugekind.h"/USE PARKIND2  ,ONLY : JPRH/ ;
#    }
#    else {
#      s/^#include +"hugekind.h"// ;
#    }
  }
}

#==========================================================================
sub readfile  {
# Read file 
  my($fname)=@_;
  my(@lines);
  if(!open(INFIL,$fname)) {
    print STDERR "Can't open $fname for reading\n";
    die("Can't open $fname for reading\n");
  }
  @lines=<INFIL>;
  close INFIL;
  (@lines);
}

#==========================================================================
sub writefile  {
# Write file
  my($fname,$lines)=@_;
  if(!open(OUTFIL,">".$fname)) {
    print STDERR "Can't open $fname for writing\n";
    exit;
  }
  print OUTFIL @$lines;
  close OUTFIL;
}

#==========================================================================
sub expcont {
#
# Expand continuation lines into statements for free-format Fortran while
# maintaining line-breaking and all comments
# Put statements onto array of references to anonymous hashes as key 'statement'
# Also put into the hash the linenumber of first line of statement as key 'first_line'
  my ($lines, $statements) = @_;
  my ($statm, $first_line);

  my $prev        = 0;
  my $line_number = 0;

  for (@$lines) {
    $line_number++;

    s/^([^'"]*)(?:\s*!.*)$/$1/; # Remove trailing comments

    s/^(\s*)&(.*)$/$1$2/s;

    if (!/^\s*!.*$/ && /^.+?&(?:\s*!.*)*\s*$/) {
      s/(.+?)&(.+)/$1\n/s;

      $statm     .= $_;
      $first_line = $line_number unless $prev;
      $prev       = 1;
      next;

    } elsif ($prev && /^\s*(?:!|$)/) { # ignore blank/comment lines
      next;

    } else {
      s/!.*?$//;

      $statm     .= $_;
      push @$statements, {
        'statement'  => $statm,
        'first_line' => $prev ? $first_line : $line_number,
      };

      $statm = "";
      $prev  = 0;
    }
  }
}
#==========================================================================

sub cont_lines {
#
# Put back continuation character in correct place and execute delayed actions
#
  my($statements,$lines,$line_hash) = @_;
  my(@temp,$i,$iup,$href);


# Put back continuation characters and split statements into lines as they were
  @$lines=();
  @$line_hash=();
  foreach $href (@$statements) {
    $_=$href->{statement};
    if (/\n.*\n/){                      # This is a multi-line statement
      @temp=split /\n/;                 # Split statement into lines (removes EOL)
      $iup=scalar(@temp);               # Number of lines in statement
      for ($i=0;$i < $iup;$i++) {       # Loop through lines
	$_=$temp[$i];
	if($i == 0 ){                   # First line
	  if(/^([^!]+)(!.*)$/) {        # Line has trailing comment
	    s/^([^!]+)(!.*)$/$1&$2\n/;  # Put back & at end of line before comment
	  }
	  else {                        # No trailing comment
	    s/^([^!]+)$/$1&\n/;         # Put back & and EOL at end of line
	  }	    
	}
	elsif ($i == ($iup-1)) {        # Last line
	  s/^( *)(.*)$/$1& $2 \n/;      # Put back & at beginning of line
	}
	else {                          # Other lines
	  if (/^ *!/) {                 # Line is comment line
	    $_=$_."\n";                 # Restore EOL for comments
	  }
	  else {
	    if(/^( *)([^!]*)(!.*)$/) {  # Line has trailing comment
	      s/^( *)([^!]*)(!.*)*$/$1& $2&$3\n/;  # & at beginning and end of line
	    }
	    else {                      # No trailing comment
	      s/^( *)([^!]*)$/$1& $2&\n/; # & at beggining and end of line
	    }	 
	  }  
	}
	if($i == 0        && exists $href->{pre_insert}) {
	  my @templines=split('\n',$href->{pre_insert});
	  foreach my $tline (@templines) {
	    my $rec={};
	    $rec->{'content'}='unknown';
	    $rec->{'line'}=$tline."\n";
	    push(@$lines,$rec->{'line'});
	    push(@$line_hash,$rec);
	  }
	}
	unless(exists $href->{remove}) {
	  my $rec={};
	  $rec->{'line'}=$_;
	  if($i == 0) {
	    $rec->{'content'}=$href->{content};
	  }
	  else {
	    $rec->{'content'}='cont_line';
	  }
	  push(@$lines,$rec->{'line'});
	  push(@$line_hash,$rec);
	}
	if($i == ($iup-1) && exists $href->{post_insert}) {
	  my @templines=split('\n',$href->{post_insert});
	  foreach my $tline (@templines) {
	    my $rec={};
	    $rec->{'content'}='unknown';
	    $rec->{'line'}=$tline."\n";
	    push(@$lines,$rec->{'line'});
	    push(@$line_hash,$rec);
	  }
	}
      }
    }
    else {  # Not multiline statement
      if(exists $href->{pre_insert}) {
	my @templines=split('\n',$href->{pre_insert});
	foreach my $tline (@templines) {
	  my $rec={};
	  $rec->{'content'}='unknown';
	  $rec->{'line'}=$tline."\n";
	  push(@$lines,$rec->{'line'});
	  push(@$line_hash,$rec);
	}
      }
      unless(exists $href->{remove}) {
	my $rec={};
	$rec->{'line'}=$_;
	$rec->{'content'}=$href->{content};
	push(@$lines,$rec->{'line'});
	push(@$line_hash,$rec);
#	print $rec;
      }
      if(exists $href->{post_insert}) {
	my @templines=split('\n',$href->{post_insert});
	foreach my $tline (@templines) {
	  my $rec={};
	  $rec->{'content'}='unknown';
	  $rec->{'line'}=$tline."\n";
	  push(@$lines,$rec->{'line'});
	  push(@$line_hash,$rec);
	}
      }
    }
  }
}
#==========================================================================
sub getvars {
# Return list of locally declared variables with type and scope information
# 
  my($statements,$prog_info,$vars,$use_vars) = @_;
  my ($test,$type,@vars1,$func,$prog_unit,$dum,$tmp_name,@pu_args);
  my ($preserve,$rank,$href);
  our($nest_par,$name);

  %$vars=();
  $func="";
  $prog_unit=0;
  %$use_vars=();
  foreach $href (@$statements) {
    next if($href->{content} eq 'comment');           # Skip comments
    next if($href->{exec});                        # Don't look in executable statements
    next if($$prog_info{is_module} and ! $href->{in_contain}); # Unless inside CONTAIN skip module 
    $prog_unit=$href->{prog_unit};
    if($href->{content} eq 'FUNCTION') {
      $_=$href->{statement};
      my $dum=&parse_prog_unit(\$func,\@pu_args);          # Get name of FUNCTION
#      print "GETVARS FUNCTION $func \n";
      $func=uc($func);
    }
    if($href->{decl} == 2 or $href->{content} eq 'EXTERNAL'){  # Real parse starts
      $_=$href->{statement};
      $_=uc($_);                                   # Upcase to avoid /.../i
      s/^ *//;                                     # remove leading blanks
      if($href->{decl} == 2) {
	$type=lc(substr($href->{content},0,1));
      }
      else {
	$type='e';
      }
      s/\!.*\n/\n/g;                               # Remove trailing comments in all lines
      $preserve=$_;
      s/(.+)::(.+)/$2/s;                           #REAL(KIND=JPRB) :: zsig(:) -> zsig(:),
      s/^EXTERNAL (.+)$/$1/;
      s/\s+//g;                                    # Remove all white-space
      if($href->{content} eq 'CHARACTER') {
	s/($name)\*\d+/$1/g;
	s/($name)\*$nest_par/$1/g;
	s/($name)$nest_par\*\w+/$1/g;
      }
      s#=\(/.+/\)##;      # ZVAL(1:2)=(/1.0,2.0/) -> ZVAL(1:2)
#?      s/=[^,\n]+//g;
      s/$nest_par//g;     # ISEC3(SIZE(NSEC3)),ISEC4(SIZE(NSEC4)) -> ISEC3,ISEC4
      s/=\w+//g;          # ZVAL=1.0 -> ZVAL
      s@/.*/@@;           # What?
      @vars1=split(',',$_);
      for(@vars1) {
	next unless /^$name$/;          # A bit of security
	if($preserve =~ /\b$_\b *\(/ | $preserve =~ /DIMENSION/) {
	  $rank=1;        # Variable is array
	}
	else {
	  $rank=0;        # Variable is scalar
	}
	if($_ eq $func) {
	  $$vars{$_}{type_spec}="f";
	} 
	else {
	  if($href->{content} eq 'FUNCTION') {
	    $$vars{$_}{type_spec}='f';
	  }
	  else {
	    $$vars{$_}{type_spec}=$type;
	  }
	}
	$$vars{$_}{scope}=$prog_unit;
	$$vars{$_}{rank}=$rank;
	$$vars{$_}{usage}='local';
      }
    }
# Perhaps the variable is really a statement function?
    if($href->{decl} == 5) {
      $_=$href->{statement};
      s/\s+//g;                                    # Remove all white-space
      /^($name)\((.+)\)=/i;
      my $tvar=uc($1);
      my @stmf_args=split(',',$2);
      if (exists($$vars{$tvar})) {
	$$vars{$tvar}{type_spec}='s';
#	print "STATMF OK $tvar \n ";
      }
      for (@stmf_args) {
	if (exists($$vars{$_})) {
	  $$vars{$_}{type_spec}='s';
#	  print "STATMF ARG OK $_ \n ";
	}
      }
    }
  }
# Perhaps instead the variable is a declaration of an external function?
  my @extract=();                  # Extract part of statements for efficiency
  foreach $href (@$statements) {
    if($href->{exec}) {                 # Function call must be in executable stat.
      next if($href->{content} eq 'CALL'); # A call can't contain an undeclared function
      push(@extract,$href->{statement});
    }
  }
  
  foreach my $var (keys (%$vars)) {
    next if($$vars{$var}{rank} > 0);   # Can't be a function if rank > 0
    next if($$vars{$var}{type_spec} eq 's' | $$vars{$var}{type_spec} eq 'f');
    my $dec_unit=$$vars{$var}{scope};
    my $regex1=qr/\b$var\b\s*\(/i;      # As var's rank=0 this could be function call
    for(@extract) {
      if(/${regex1}/) {
	s/\!.*\n/\n/g;                       # Remove trailing comments in all lines
	s/\s+//g;                            # Remove all white-space
	if(/${regex1}/) {
	  if($$vars{$var}{type_spec} eq 'c') {   # Avoid CLVAR(1:3) etc.
	    next if(/${regex1}\s*(\d+|$name)*\s*:\s*(\d+|$name)*\s*\)/);
	  }
#	  print "TYPE changed to function $var $_ \n";
	  $$vars{$var}{type_spec}='f';
	  last;
	}
      }
    }
  }
# ---------------------------------------------------------------------
# Assign  "usage" in Doctor sense to variable (default usage is 'local')
# 
  foreach $href (@$statements) {
# Is the varaible a dummy argument
    if($href->{content} eq 'FUNCTION' or $href->{content} eq 'SUBROUTINE') {
      $_=$href->{statement};
      @pu_args=();
      my $dum=&parse_prog_unit(\$func,\@pu_args);   # Get arguments
      for(@pu_args) {
	if( exists $$vars{$_} ) {
	  if($$vars{$_}{scope} == $href->{prog_unit}) {
	    $$vars{$_}{usage}='arg';
	  }

	} else {
          print STDERR "Argument $_ has not got a corresponding declaration " .
                       "statement\n";
          print STDERR "Bailing out at this point\n";
          die "Bailing out";
	}
      }
    }
# Does the variable appear in a NAMELIST
# We want to distinguish this for more lenient Doctor check
    if($href->{content} eq 'NAMELIST') {
      $_=$href->{statement};
      s/\!.*\n/\n/g;     # Remove trailing comments in all lines
      s/\s+//g;          # Remove all white-space
      m:NAMELIST/\w+/(.+):;
      my @namvars=split(',',uc($1));
      for (@namvars) {
	if( exists $$vars{$_} ) {
	  if($$vars{$_}{scope} == $href->{prog_unit}) {
	    $$vars{$_}{usage}='namvar';
	  }
	}
      }
    }
    if(exists $href->{inc_statm}) { # We also have to look in include files
      my $incs=$href->{inc_statm};
      foreach my $hrefi (@$incs) {
	if($hrefi->{content} eq 'NAMELIST') {
	  $_=$hrefi->{statement};
	  s/\!.*\n/\n/g;     # Remove trailing comments in all lines
	  s/\s+//g;          # Remove all white-space
	m:NAMELIST/\w+/(.+):;
	  my @namvars=split(',',uc($1));
	  for (@namvars) {
	    if( exists $$vars{$_} ) {
	      if($$vars{$_}{scope} == $href->{prog_unit}) {
		$$vars{$_}{usage}='namvar';
	      }
	    }
	  }
	}
      }
    }
  }
# -----------------------------------------------------------------------------
# Find use variables
  my %use_count=();
  foreach $href (@$statements) {
    if($href->{content} eq 'USE') {
      $prog_unit=$href->{prog_unit};
      $_=$href->{statement};
      s/\!.*\n/\n/g;                               # Remove trailing comments in all lines
      s/\s+//g;                                    # Remove all white-space
      $_=uc($_);                                   # Upcase to avoid /.../i
      if(/^USE($name),ONLY:(.+)$/){
	my $modname=$1;
	if( exists $use_count{$modname}) {
	  if($prog_unit == $use_count{$modname}) {
	    print STDERR "-> $href->{statement}";
            print STDERR "USE $modname appears more than once in program unit $prog_unit \n\n";

	  }
	}
	$use_count{$modname} = $prog_unit;
	my @usevars = split /,/ ,$2;
	my %usevars=();
	foreach my $usevar (@usevars) {
	  $usevars{$usevar}++;
	  $$use_vars{$usevar}{module}=$modname;
	  $$use_vars{$usevar}{scope}=$prog_unit;
	  $$use_vars{$usevar}{count}++;
	}
	foreach my $usevar (keys (%usevars)) {
	  if($usevars{$usevar} >1) {
	    print STDERR "DUPLICATE USE ONLY VARIABLE ",
	    "$modname $usevar $prog_unit \n";
	    $_=$href->{statement};
	    s/\b$usevar\b//i;
	    s/,\s*,/,/;
	    s/,\s*\n$/\n/;
	    s/\n *\n/\n/;
	    s/^(.+:\s*),/$1/;
	    $href->{statement}=$_;
	  }
	}
      }
      else {
#	print "WARNING:USE without ONLY \n";
      }
    }
  }
}
#==========================================================================
sub find_unused_vars {
# Find declared variables not used
  my($statements,$vars,$unused_vars,$use_vars,$unused_use_vars) = @_;
  my ($var,@tokens,$href);
  @tokens=();
# Find all tokens in file
  foreach $href (@$statements) {
    next if($href->{content} eq 'comment');
    if(exists $href->{inc_statm}) {  # Look also in include files
      my $incs=$href->{inc_statm};
      foreach my $hrefi (@$incs) {
	die "FUV $href->{content} $href->{statement}" unless exists $hrefi->{statement};
	$_=$hrefi->{statement};
	if(/\b[a-zA-Z]\w*\b/) {
	  push(@tokens,/\b[a-zA-Z]\w*\b/g);
	}
      }
    }
    else {
      $_=$href->{statement};
      push(@tokens,/\b[a-zA-Z]\w*\b/g);
    }
  }
  @tokens= map {uc} @tokens; # Upcase array of tokens, the variables are upper-case

# Find out how many times the variable appears in array tokens
  foreach $var (keys (%$vars)) {
    $$vars{$var}{uses}=0;
  }
  foreach $var (keys (%$use_vars)) {
    $$use_vars{$var}{uses}=0;
  }
  for (@tokens) {
    if(exists($$vars{$_})){
      $$vars{$_}{uses}++; 
    }
    if(exists($$use_vars{$_})){
      $$use_vars{$_}{uses}++; 
    }
  }
# If it appears only one time (which must be in a declaration) it is unused
  @$unused_vars=();
  foreach $var (keys (%$vars)) {
    push(@$unused_vars,$var) if($$vars{$var}{uses} < 2);
  }
  @$unused_use_vars=();
  foreach $var (keys (%$use_vars)) {
    push(@$unused_use_vars,$var) if($$use_vars{$var}{uses} < 2);
  }
}
#==========================================================================
sub remove_unused_vars {
# Does what it says on the tin
  my($statements,$unused_vars,$unused_use_vars) = @_;
  my ($var,$href);
  our $nest_par;
  for (@$unused_vars) {
    $var=$_;
    foreach $href (@$statements) {
      $_=$href->{statement};
      next unless(($href->{decl}) | ($href->{content} eq 'comment'));
      if($href->{content} eq 'comment') {
	next unless(/^ *!\$OMP/);
      }
      if(/\b$var\b/i) {
#	print $_;
	
	if(/\b$var\b *\(/i) {
#	  print "ZYZ $var $_";
	  s/\b$var\b *$nest_par *(=\s*\(\/.*\/\))*//si;
#	  print "ZZZ $var $_";
	}
	s/\b$var\b\s*=\s*\d+(\.\d*)*//i;
	s/\b$var\b *(\* *\d+)*//i if($href->{content} eq 'CHARACTER') ;
	s/\b$var\b//i; 
#	print $_;
        s/^.+:: *\n$//;
        s/^.+:: *\!.*\n$//;
#	print $_;
        s/,\s*,/,/;
#	print $_;
	s/, *\n$/\n/;
#	print $_;
        s/(::\s*),(.+)$/$1$2/s;
        s/\n *\n/\n/;
        s/\n *!.*\n/\n/;
	s/, *\n$/\n/;
# Remove "empty" lines
        s/^.+::\s*$//;
        s/^.+::\s*=.*$//;
        s/^.+::\s*!.*$//;
#	print $_;
        s/^CHARACTER *\*\d+ *\n$//i if($href->{content} eq 'CHARACTER') ;
	$href->{statement}=$_;
      }
    }
  }
  for (@$unused_use_vars) {
    $var=$_;
    foreach $href (@$statements) {
      next unless($href->{decl} == 4);
      $_=$href->{statement};
      next if(/PARKIND/); #I am sure this could be done betterh

      if(/\b$var\b/i) {
	s/\b$var\b//i;
        s/,\s*,/,/;
        s/,\s*\n$/\n/;
        s/\n *\n/\n/;
	s/^(.+:\s*),/$1/;
	s/^.+:\s*$//;
	$href->{statement}=$_;
      }
    }
  }
}
#==========================================================================
sub tidy_decl {
# Tidy up declarions
  my($statements) = @_;
  my($href,$content);

  foreach $href (@$statements) {
    next unless($href->{decl} == 2);
    $_=$href->{statement};
    $content=$href->{content};
    
    if($content eq 'CHARACTER') {
      s/CHARACTER *\* *(\w+)/CHARACTER \(LEN = $1\)/i; 
      s/CHARACTER *\* *\(\*\)/CHARACTER \(LEN = \*\)/i;
      s/CHARACTER *\* *\( *(\w+) *\)/CHARACTER \(LEN = $1)/i;
    }
    if($content eq 'INTEGER') {
      if(/^ *INTEGER[^\(]/i) {
	s/INTEGER\b/INTEGER(KIND=JPIM)/;
      }
    }
    unless (/::/) {
      s/^( *LOGICAL )/$1:: /i;
      s/^( *INTEGER\(KIND=JPI\w\) )/$1:: /;
      s/^( *REAL\(KIND=JPR\w\) )/$1:: /;
      if(/^ *CHARACTER/i) {
	if( s/^( *CHARACTER *\( *LEN *= *\w+ *\))/$1 :: /i) {
	  $href->{statement}=$_;
	  next;
	}
	if(s/^( *CHARACTER *\( *LEN *= *\* *\))/$1 :: /i) {
	  $href->{statement}=$_;
	  next;
	}
	s/^( *CHARACTER )/$1:: /i;
      }
    }
    $href->{statement}=$_;
  }
}
#==========================================================================

sub doctor_viol {
# Find Doctor violations

  my($vars,$fix_doc) = @_;
  my ($var,$type,$zz,$prog_unit,$usage);
  %$fix_doc=();

  foreach $var (keys (%$vars)) {
    $type=$$vars{$var}{type_spec};
    $prog_unit=$$vars{$var}{scope};
    $usage=$$vars{$var}{usage};
#    print "DOC $var $type $prog_unit $usage \n";
    if($zz=&doc_char($type,$usage,$var)) {
#      print "DOCTOR VIOL - ",$var," $type $zz $prog_unit\n";
      $$fix_doc{$var}=$zz.'_'.$var.','.$prog_unit
    }
  }  
}
#==========================================================================

sub fix_doctor_viol {
# Fix Doctor violations
  my($statements,$fix_doc) = @_;
  my($doc_viol,$repl,$prog_unit,$cur_prog_unit,@allowed,$href,$content);
  my($tmp_name,@pu_args);

  @allowed=('NRGRI'); # Hack

  VIOL:foreach $doc_viol (keys (%$fix_doc)) {
    # Let's allow some violations
    for (@allowed){ 
      next VIOL if($doc_viol eq $_);
    }

    ($repl,$prog_unit)=split(',',$$fix_doc{$doc_viol});

    print "FIX $repl $prog_unit \n";
    foreach $href (@$statements) {
      $content=$href->{content};
      $_=$href->{statement};
      if($href->{content} eq 'comment') {
	next unless(/^ *!\$OMP/);
      }
      $cur_prog_unit=$href->{prog_unit};
      if($prog_unit == $cur_prog_unit) {  # Could be fine in other program units
	if(/\b$doc_viol\b/i) {
	  s/%$doc_viol\b/_X_$doc_viol/ig; # Protect type-components
	  s/\b$doc_viol\b/$repl/ig;
	  s/_X_$doc_viol\b/%$doc_viol/ig; # Restore type-components
	}
      }
      $href->{statement}=$_;
    }
  }
  
}
#==========================================================================
sub various{
# 
  my($statements,$prog_info,$vars) = @_;
  my($punit,@args,$tmp_name,$cont,$statm);
  my($href,$exec);
  our $nest_par;
#------------------------------------------------------------------
# Remove unneccesary RETURN statement
  foreach $href (@$statements) {
    $cont=$href->{content};
    if($cont eq 'RETURN') {
      if($href->{exec} == 3) {   # $href->{exec} == 3 means last executable statement
	$href->{remove} = 1;     # Post remove line for later
      }
    }
  }


# Make sure all CALL MPL_... has a CDSTRING argument
  foreach $href (@$statements) {
    $cont=$href->{content};
    if($href->{content} eq 'CALL' ) {
      $_=$href->{statement};
      if(/^\s*CALL\s+MPL_/i) {
	next if(/^\s*CALL\s+MPL_ABORT/i);
	next if(/^\s*CALL\s+MPL_WRITE/i);
	next if(/^\s*CALL\s+MPL_READ/i);
	next if(/^\s*CALL\s+MPL_OPEN/i);
	next if(/^\s*CALL\s+MPL_CLOSE/i);
	next if(/^\s*CALL\s+MPL_INIT/i);
	next if(/^\s*CALL\s+MPL_GROUPS_CREATE/i);
	next if(/^\s*CALL\s+MPL_BUFFER_METHOD/i);
	next if(/^\s*CALL\s+MPL_IOINIT/i);
	next if(/^\s*CALL\s+MPL_CART_COORD/i);
#	print "CDSTRING=$$prog_info{'unit_name'}[$href->{prog_unit}]: \n";
	unless(/CDSTRING\s*=/i) {
	  s/\)(\s)$/,CDSTRING=\'$$prog_info{'unit_name'}[$href->{prog_unit}]:\'\)$1/;
	  $href->{statement}=$_;
	}
      }
    }
  }
	


#------------------------------------------------------------------
# Add Standard Modification Line

  my $start=0;
  foreach $href (@$statements) {
    $cont=$href->{content};
    if($cont eq 'comment') {
      $_=$href->{statement};
      if($start) {                        # Found header - look for end of mod lines
	if(/^ *$/ || /^! *------------------------/) {
	  $href->{pre_insert} = "!        M.Hamrud      01-Oct-2003 CY28 Cleaning\n";
	  last;
	}
	next;
      }
      $start=1 if(/^! +Modifications/i) ;  # This how the header should look
      next;
    }
    last if($href->{exec});                # We have failed - bail out
  }

# Change subroutine and call multi-line statements so that the comma
# beetwen variables comes at the end of the line
  my @lines=();
  foreach $href (@$statements) {
    if(exists $href->{multi_line}) {
      $cont=$href->{content};
      if($cont eq 'SUBROUTINE' | $cont eq 'CALL' ) {
	$statm=$href->{statement};
	@lines=split "\n", $statm;
	@lines = reverse @lines;
	my $append_comma=0;
	for (@lines) {
#	  print "A $append_comma $_ \n";
	  next if(/^ *!/);
	  if($append_comma) {
	    if(/\S *!.*$/) {
	      s/(\S)( *!.*)$/$1,$2/;
	    }
	    else {
	      s/(\S) *$/$1,/;
	    }
	  }
	  $append_comma=s/^ *,//;
#	  print "B $append_comma $_ \n";
	}
	@lines = reverse @lines;
	$statm=join  "\n",@lines;
	$statm=$statm."\n";
	$href->{statement}=$statm;
      }
    }
  }
  our $name;
  foreach $href (@$statements) {
    if($href->{content} eq 'USE') {
      $_=$href->{statement};
      unless(/^\s*USE\s+$name\s*,\s*ONLY\s*:/i){
	print $_;
	print "WARNING:USE without ONLY \n";
      }
    }
  }    
}
#==========================================================================
sub insert_hook{
# 
  my($statements,$prog_info,$vars) = @_;
  my($punit,@args,$tmp_name,$cont,$statm);
  my($href,$exec);
  our $nest_par;
#------------------------------------------------------------------
# Add HOOK function
  my $unit_name='';
  my $last_use=0;
  my $hook_status=0;
  my $in_contain=0;
  my $prev_prog=0;
  my ($decl,$remember);
  foreach $href (@$statements) {
    $cont=$href->{content};
    next if($cont eq 'comment');

    $decl=$href->{decl};
    $exec=$href->{exec};
    $in_contain=$href->{in_contain};
    if(! $in_contain and $href->{prog_unit} > $prev_prog) {
      $hook_status=0;
      $prev_prog=$href->{prog_unit};
      print "resetting hook status \n";
    }

    if($cont eq 'FUNCTION' or $cont eq 'SUBROUTINE' or 
       $cont eq 'PROGRAM'){ # Need name of routine
      $_=$href->{statement};
      &parse_prog_unit(\$unit_name,\@args);
      $unit_name=uc($unit_name);
# If in module pre-pend module name
      $unit_name=$$prog_info{module_name}.':'.$unit_name if($$prog_info{is_module}); 
      $remember=0;
    }

    if($hook_status == 0) {   # $hook_status == 0 means we have not done anything yet
      if($cont eq 'USE') {    # Add USE YOMHOOK as second use statement
	$href->{post_insert}="USE YOMHOOK   ,ONLY : LHOOK,   DR_HOOK\n";
	$hook_status=1;
      }
      elsif($cont eq 'IMPLICIT NONE') { # No previous USE, add USE YOMHOOK before IMPLICIT NONE
	$href->{pre_insert} ="USE PARKIND1  ,ONLY : JPRB\n".
	  "USE YOMHOOK   ,ONLY : LHOOK,   DR_HOOK\n";
	$hook_status=1;
      } 
    }
    $remember=$href->{number} if($decl == 2); 

#   Use statement added ($hook_status == 1), now insert HOOK switch on statement
#   before first executable statement in program unit ($exec == 2)
    if($hook_status == 1 && $exec == 2) {
      if($remember) {
	$$statements[$remember]->{post_insert}="REAL(KIND=JPRB) :: ZHOOK_HANDLE\n";
	$href->{pre_insert}="IF (LHOOK) CALL DR_HOOK(\'${unit_name}\',0,ZHOOK_HANDLE)\n";
      }
      else {
	$href->{pre_insert}="REAL(KIND=JPRB) :: ZHOOK_HANDLE\n".
	    "IF (LHOOK) CALL DR_HOOK(\'${unit_name}\',0,ZHOOK_HANDLE)\n";
      }   
      if($cont eq 'IF') {
	if($href->{content2} eq 'RETURN') {
	  $_=$href->{statement};
	  s/(\s*IF\s*$nest_par).*\n/$1/i;
	  s/\)$/ .AND. LHOOK\)/;
	  $href->{pre_insert}=$href->{pre_insert}."$_ CALL DR_HOOK(\'${unit_name}\',1,ZHOOK_HANDLE)\n";
	}
      }
      $hook_status=2;
    }
#   Hook switched on($hook_status == 2), switch off after last executable statement
#   ($exec == 3)
    elsif($hook_status == 2) {
      if($exec == 3 or $exec == 23) {
	$href->{post_insert}="IF (LHOOK) CALL DR_HOOK(\'${unit_name}\',1,ZHOOK_HANDLE)\n";
	$hook_status=3;
      }
      elsif($cont eq 'RETURN') {
	$href->{pre_insert}="IF (LHOOK) CALL DR_HOOK(\'${unit_name}\',1,ZHOOK_HANDLE)\n";
      }
      elsif($cont eq 'IF') {
	if($href->{content2} eq 'RETURN') {
	  $_=$href->{statement};
	  s/(\s*IF\s*$nest_par).*\n/$1/i;
	  s/\)$/ .AND. LHOOK\)/;
	  $href->{pre_insert}="$_ CALL DR_HOOK(\'${unit_name}\',1,ZHOOK_HANDLE)\n";
	}
      }	
    }
    $hook_status=1 if($in_contain && $hook_status==3); # Reset hook status in CONTAIN region
  }
  die "Adding HOOK function failed " if($hook_status == 2);
}
#==========================================================================

sub doc_char{
# Returns suggested prefix in case of DOCTOR violation (otherwise null string)
  my($type,$usage,$var) = @_;
  my $prefix="";
# INTEGER variables
  if( $type eq "i") {
    if($usage eq "arg") {
      $prefix="K" unless($var=~/^K/i);
    }
    elsif($usage eq "local") {
      $prefix="I" unless($var=~/^[IJ]/i);
    }
    elsif($usage eq "module") {
      $prefix="N" unless($var=~/^[MN]/i);
    }
    elsif($usage eq "namvar") {
      $prefix="I" unless($var=~/^[MNIJ]/i);
    }
    else { 
      die "Unknown usage";
    }
  }
# REAL variables
  elsif( $type eq "r") {
    if($usage eq "arg") {
      $prefix="P" unless($var=~/^P/i);
    }
    elsif($usage eq "local") {
      $prefix="Z" unless($var=~/^Z|^PP/i);
    }
    elsif($usage eq "module") {
      $prefix="R" if ($var=~/^[ZPIJKLMNCY]/i);
    }
    elsif($usage eq "namvar") {
      $prefix="Z" if ($var=~/^[PIJKLMNCY]/i);
    }
    else { 
      die "Unknown usage";
    }
  }
#LOGICAL variables
  elsif( $type eq "l") {
    if($usage eq "arg") {
      $prefix="LD" unless($var=~/^LD/i);
    }
    elsif($usage eq "local") {
      $prefix="LL" unless($var=~/^LL/i);
    }
    elsif($usage eq "module") {
      $prefix="L" unless($var=~/^L[^LD]/i);
    }
    elsif($usage eq "namvar") {
      $prefix="LL" unless($var=~/^L/i);
    }
    else { 
      die "Unknown usage";
    }
  }
#CHARACTER variables
  elsif( $type eq "c") {
    if($usage eq "arg") {
      $prefix="CD" unless($var=~/^CD/i);
    }
    elsif($usage eq "local") {
      $prefix="CL" unless($var=~/^CL/i);
    }
    elsif($usage eq "module") {
      $prefix="C" unless($var=~/^C[^LD]/i);
    }
    elsif($usage eq "namvar") {
      $prefix="CL" unless($var=~/^C/i);
    }
    else { 
      die "Unknown usage";
    }
  }
# USER DEFINED TYPES
  elsif( $type eq 't') {
    if($usage eq "arg") {
      $prefix="YD" unless($var=~/^YD/i);
    }
    elsif($usage eq "local") {
      $prefix="YL" unless($var=~/^YL/i);
    }
    elsif($usage eq "module") {
      $prefix="Y" unless($var=~/^Y[^LD]/i);
    }
    elsif($usage eq "namvar") {
      $prefix="YL" unless($var=~/^Y/i);
    }
    else { 
      die "Unknown usage";
    }
  }
# FUNCTION/EXTERNAL declarations
  elsif( $type eq 'f' || $type eq 'e' || $type eq 's') {
# Everything is OK
  }
  else {
    die "Unknown type $type"
  }
  ($prefix);
}
#==========================================================================
     
sub parse_prog_unit {
  # Find out type (return), name ($$unit_name) and arguments (@$args)
  # from a program unit statement ($_)
  my ($unit_name, $args) = @_;
  $$unit_name = '';
  @$args      = ();

  my $type = '';

  our ($name, $type_spec, $attribute);

  if (/^\s*(MODULE|PROGRAM|BLOCK\s*DATA)\s+($name)\s*$/io) {
    $type       = lc ($1);
    $$unit_name = $2;

    # Remove space from "block data"
    $type       =~ s/\s*//;

  } elsif (/^\s*(?:$attribute)?\s*(SUBROUTINE)\s+($name)\b/io or
           /^\s*(?:$attribute)?\s*(?:$type_spec)?\s*(FUNCTION)\s+($name)\b/io) {
    $type       = lc ($1);
    $$unit_name = $2;

    # Get arguments/keywords from SUBROUTINE/FUNCTION
    if(/^[^\(]+\([^\)]+\)/) {
      my $tstatm = $_;

      # Remove trailing comment
      $tstatm =~ s/\!.*\n/\n/g;

      # Remove space characters
      $tstatm =~ s/\s//g;

      # Add the RESULT clause to the argument/keyword list
      $tstatm =~ s/\)result\((\w+\))$/,$1/i if $type eq 'function';

      # Remove the parenthesis around the argument list
      $tstatm =~ s/.+\((.+)\)/$1/;

      @$args = split (',', uc ($tstatm));

      # For FUNCTION, add its name to the list if necessary
      push @$args, uc ($$unit_name)
        if $type eq 'function' and not grep {$_ eq uc ($$unit_name)} @$args;
    }
  }

  return $type;
}

#==========================================================================

sub setup_parse {
  # Set up some "global" variables that helps with parsing statements

  # Pattern for nested parenthesis
  our $nest_par;
  $nest_par = qr/\((?:(?>[^()]+)|(??{$nest_par}))*\)/; #Camel p214

  # Patterns for variable name and natural digit
  our $name='[a-zA-Z]\w*';
  our $digit_string='\d+';
  our $type_name=$name;

  # Patterns for specification
  our $specification_expr='(?:'.$name.'|'.$digit_string.')'; # Simplification
  our $type_param_value='(?:\*|'.$specification_expr.')';

  # Patterns for length/kind attributes
  our $char_selector='LEN *= *'.$type_param_value; # Simplification
  our $kind_selector='\( *KIND *= *'.$name.' *\)';    # Simplification

  # Pattern for type specification
  our $type_spec='INTEGER *(?:'.$kind_selector.')?|REAL *(?:'.$kind_selector.
    ')?|DOUBLE PRECISION|COMPLEX *(?:'.$kind_selector.')?|CHARACTER *'.
    $char_selector.'|LOGICAL *(?:'.$kind_selector.')?|TYPE\s*\(\s*'.$type_name.
    '\s*\)';

  # Pattern for function/subroutine attribute
  our $attribute = 'ELEMENTAL|(?:RECURSIVE(?:\s+PURE)?|PURE(?:\s+RECURSIVE)?)';

  return;
}

#==========================================================================

sub f90_indent {
# Indent free-format F90 program to our standards
  my($line_hash,$lines)=@_;
  my($delta)='  '; 
  my($cur_indent)='';
  @$lines=();
  foreach my $href (@$line_hash) {
    $_=$href->{line};
    if($href->{content} eq 'comment') {
      push(@$lines,$_);
      next;
    }
    s/^ *//; # Remove current indentation
    my($post_chg)=0;
    my($pre_chg)=0;
    my($cont_line)='';
    exit if (! exists $href->{content});
    if($href->{content} eq 'DO') {
      $post_chg=1 unless /^DO\s+\d/;
    }
    elsif($href->{content} eq 'ENDDO') {
      $pre_chg=1;
    }
    elsif($href->{content} eq 'IF_construct') {
      $post_chg=1;
    }
    elsif($href->{content} eq 'ELSEIF') {
      $post_chg=1;
      $pre_chg=1;
    }
    elsif($href->{content} eq 'ELSE') {
      $post_chg=1;
      $pre_chg=1;
    }
    elsif($href->{content} eq 'ENDIF') {
      $pre_chg=1;
    }
    elsif($href->{content} eq 'ENDIF') {
      $pre_chg=1;
    }
    elsif($href->{content} eq 'WHERE_construct') {
      $post_chg=1;
    }
    elsif($href->{content} eq 'ELSEWHERE') {
      $post_chg=1;
      $pre_chg=1;
    }
    elsif($href->{content} eq 'ENDWHERE') {
      $pre_chg=1;
    }
    elsif($href->{content} eq 'ENDIF') {
      $pre_chg=1;
    }
    elsif($href->{content} eq 'SELECT CASE') {
      $post_chg=1;
    }
    elsif($href->{content} eq 'CASE') {
      $post_chg=1;
      $pre_chg=1;
    }
    elsif($href->{content} eq 'END SELECT') {
      $pre_chg=1;
    }
    $cont_line=' ' if($href->{content} eq 'cont_line');
    if( $pre_chg ) {
      unless($cur_indent=~s/^$delta//o) {
	print STDERR $_;
	die  "f90_indent: something wrong, indent negative\n";;
      }
    }
#    print "$cur_indent$cont_line$_";
    
    $_=$cur_indent.$cont_line.$_;
    push(@$lines,$_);
    $cur_indent.=$delta if( $post_chg );
  }

  if(! ($cur_indent eq '')) {
    die "f90_indent: something wrong, indent=XX${cur_indent}XX\n";
  }
}

#==========================================================================

sub tidy {
# Straigthforward tidiyng of statements
  my($statements) = @_;
  my($href,$content);
  foreach $href (@$statements) {
    $_=$href->{statement};
    $content=$href->{content};
# Substitute tab with four blanks
    s/\t/    /g;
    if($content eq 'comment') {
# Substitute empty comment line with empty line
      s/^[!] *\n$/\n/;
      $href->{statement}=$_;
      next;
    }
    if($href->{exec}) {
      if($content eq 'ENDDO') {
	s/\bEND DO\b/ENDDO/i;
	$href->{statement}=$_;
	next;
      }
      if($content eq 'ENDIF') {
	s/\bEND IF\b/ENDIF/i;
	$href->{statement}=$_;
	next;
      }
      if($content eq 'ENDWHERE') {
	s/\bEND WHERE\b/ENDWHERE/i;
	$href->{statement}=$_;
	next;
      }

      s/\bELSE IF\b/ELSEIF/i  if($content eq 'ELSEIF');

      if(/\./) {
	s/ *\.EQ\. */ == /gi;
	s/ *\.NE\. */ \/= /gi;
	s/ *\.LT\. */ < /gi;
	s/ *\.LE\. */ <= /gi;
	s/ *\.GT\. */ > /gi;
	s/ *\.GE\. */ >= /gi;
      }

#
      s/\bA?MAX[01]\b/MAX/gi;
      s/\bA?MIN[01]\b/MIN/gi;
      s/\bAMOD\b/MOD/gi;
      s/\bALOG\b/LOG/gi;
      s/\bALOG10\b/LOG10/gi;
#      s/\bI(SIGN *\()/$1/gi; # Goes wrong in larcinad etc.
      s/\bFLOAT\b/REAL/g;
      s/\bfloat\b/real/g;
    }
    
    $href->{statement}=$_;
  }
}

#==========================================================================

sub process_include_files {
# Read include files and put reference to the anonomys array
# holding the array of "statement" hashes in $href->{inc_statm}
  my($statements,$prog_info,$inc_statements) = @_;
  my ($content,$fname,$href);
  return unless ($$prog_info{has_include});
  my @lines=();
  foreach $href (@$statements) {
    $content=$href->{content};
    if($content eq 'include'){
      $_=$href->{statement};
      /["](\S+)["]/;
      $fname=$1;
      &get_inc_lines($fname,\@lines);
# Macro-removal
      &remove_macro(\@lines);
# Expand lines into statements and put refernce to this
# array of hashes into $href->{inc_statm}
      my @inc_statms=();
      my $dum={};
      &expcont(\@lines,\@inc_statms);
      $href->{inc_statm}=[@inc_statms];
      my $incs=$href->{inc_statm};
# Study the read in file and add more attributes
      &study($incs);
#      print Dumper($incs,$dum);
      
    }
  }
}
#==========================================================================
sub get_inc_lines{
# Recurcivly get lines from include files, flatten into array of lines
  my ($fname,$lines) = @_;
  my ($VPATH,@vpath,@tmp_lines);

  $VPATH=$ENV{VPATH} or die "VPATH not defined ";
# IFS VPATH /tmp/27/ifs/function:/tmp/27/ifs/common:/tmp/27/ifs/interface:/tmp/27/ifs/namelist:/tmp/27/ifsaux/include:/tmp/27/trans/interface:/tmp/27/obsort/interface:/tmp/27/ifs/ald_inc/function:/tmp/27/ifs/ald_inc/interface:/tmp/27/ifs/ald_inc/namelist
  @vpath=split(":",$VPATH);
# Look for include file in VPATH
  foreach my $path (@vpath) {
    my $ffname=$path.'/'.$fname;
    if( -f $ffname) {
# Read lines from include file
      @tmp_lines = &readfile($ffname);
#      print "$ffname \n";
      for (@tmp_lines) {
	if(/^\#include\b/){
	  /["](\S+)["]/;
	  my $fname2=$1;
	  &get_inc_lines($fname2,$lines);
	}
	else {
	  push(@$lines,$_);
	}
      }
      last;
    }
  }
  die "Include file $fname not found in VPATH=$VPATH " unless(@$lines);
}

# ------------------------------------------------------------------------------
# SYNOPSIS
#   &create_interface_block (\@statements, \@interface_block);
#
# DESCRIPTION
#   This function analyses the Fortran statements in \@statements and returns
#   an interface block in \@interface_block.
# ------------------------------------------------------------------------------

sub create_interface_block {
  # Create a "minimal" interface block for subroutines
  my  ($statements, $interface_block) = @_;
  my  (%pu_args, %tokens);
  our ($name, $nest_par);

  @$interface_block = ();

  my @tokens_in_lines = (); # List of tokens in each line

  # Gather information needed to create interface block for routine
  for my $href (@$statements) {
    last if $href->{exec}; # exit loop at beginning of executable statements

    # Get arguments of subroutine or function
    if ($href->{content} eq 'SUBROUTINE' or $href->{content} eq 'FUNCTION') {
      my $func;
      my @pu_args;
      $_ = $href->{statement};
      &parse_prog_unit (\$func, \@pu_args);
      $pu_args{uc ($_)} = 1 for @pu_args;
      next;
    }

    # Get tokens from lines where arguments are present
    # Inspect only type declaration statements
    next unless $href->{decl} == 2;

    my $statement = uc $href->{statement};
    $statement =~ s/!.*$//; # Remove trailing comment

    my @line_tokens = ();
    if ($statement =~ s/^(.*?):://) {
      # New style declaration statement contains "::"

      # Tokens in specification part
      my $spec = $1;
      my @tokens = ($spec =~ /\b$name\b/g);
      shift @tokens; # Remove leading token

      for (@tokens) {
        push @line_tokens, $_
          unless /^(?:KIND|LEN|ALLOCATABLE|POINTER|TARGET|DIMENSION|OPTIONAL|
                  SAVE|INTENT|IN|OUT|INOUT|PARAMETER)$/x;
      }

      # Tokens in declaration part
      push @line_tokens, ($statement =~ /\b$name\b/g);

    } else {
      # Old style declaration statement does not contain "::"
      @line_tokens = ($statement =~ /\b$name\b/g);
      shift @line_tokens; # Remove leading token
    }

    push @tokens_in_lines, \@line_tokens;

    # Check whether each token matches an argument
    for my $token (@line_tokens) {
      if (exists $pu_args{$token}) {
        $tokens{$_} = 1 for @line_tokens;
        last;
      }
    }
  }

  # Parse statements one more time to ensure all required tokens are included
  for (@tokens_in_lines) {
    my @line_tokens = @{ $_ };

    # Check whether line contains an essential token
    for my $token (@line_tokens) {
      if (exists $tokens{$token}) {
        $tokens{$_} = 1 for @line_tokens;
        last;
      }
    }
  }

  # Create the interface block
  for my $href (@$statements) {
    my %myhref  = %$href;
    my $content = $myhref{content};

    # Ignore comment, executable statements and items in CONTAINS block
    next if $content eq 'comment';
    next if $myhref{exec};
    next if $myhref{in_contain};

    # Delete existing pre- and post -inserts
    delete $myhref{pre_insert}  if exists $myhref{pre_insert};
    delete $myhref{post_insert} if exists $myhref{post_insert};

    # Put SUBROUTINE/FUNCTION statement into interface block
    if ($content =~ /^(?:SUBROUTINE|FUNCTION)$/) {
      $myhref{pre_insert} = 'INTERFACE' . "\n"; # Insert INTERFACE statement
      push @$interface_block, \%myhref;
    }

    # Add USE statement in interface block, if necessary
    if($myhref{decl} == 4) {
      $_ = uc $myhref{statement};
      tr/ \n//d;

      if(/^USE$name,ONLY:(.+)$/) {
        # USE statement with ONLY, check token to see if it is necessary
	my @line_tokens = /\b$name\b/g;

	for (@line_tokens) {
	  if (exists $tokens{$_}) {
	    push @$interface_block, \%myhref;
	    last;
	  }
	}

      } else {
        # Always add USE statement without ONLY
	push @$interface_block, \%myhref;
      }  
    }

    if ($myhref{decl} == 1 or $myhref{decl} == 2) {
      $_ = uc ($myhref{statement});
      s/\s*!.*$//;

      if ($content eq 'INTEGER' or $content eq 'PARAMETER') {
        # INTEGER and PARAMETER may be used for dimensioning
	my @line_tokens = /\b$name\b/g;

	for (@line_tokens) {
	  if (exists $tokens{$_}) {
	    push @$interface_block, \%myhref;
	    last;
	  }
	}
      } else {
        # Add line only if an argument is present
	s/$nest_par//g;
	my @line_tokens = /\b$name\b/g;

	for (@line_tokens) {
	  if (exists $pu_args{$_}) {
	    push @$interface_block, \%myhref;
	    last;
	  }
	}
      }
    }

    # Add END statement to interface block
    if ($content =~ /^END\s+(?:SUBROUTINE|FUNCTION)/) {
      $myhref{post_insert} = 'END INTERFACE' . "\n";
      push @$interface_block, \%myhref;
    }
  }

  # Beautify the interface block
  for my $href (@$interface_block) {
    $_ = $href->{statement};

    s/\!.*\n/\n/g; # Remove trailing comments
    s/ +/ /g;      # Only one space
    s/\n *\n/\n/g; # Remove empty lines
    s/\n *\n/\n/g; # Remove empty lines again
    s/ +\n/\n/g;   # No trailing spaces

    $href->{statement} = $_;
  }

  return;
}

# ------------------------------------------------------------------------------

sub change_var_names{
  my($statements) = @_;
  foreach my $href (@$statements) {
    $_=$href->{statement};
    s/\bVAZX\b/YVAZX/ig;
    s/\bPVAZX\b/YDVAZX/ig;
    s/\bVAZG\b/YVAZG/ig;
    s/\bPVAZG\b/YDVAZG/ig;
    s/\bSCALP_DV\b/YSCALP/ig;
    s/\bRSCALP_DV\b/YRSCALP/ig;
    s/\bSCALPSQRT_DV\b/YSCALPSQRT/ig;
    s/\bRSCALPSQRT_DV\b/YRSCALPSQRT/ig;
    s/\bPYBAR\b/YDYBAR/ig;
    s/\bPSBAR\b/YDSBAR/ig;
    s/\bVCGLPC\b/YVCGLPC/ig;
    s/\bVCGLEV\b/YVCGLEV/ig;
    s/\bSKFROT\b/YSKFROT/ig;
    s/\bSKFMAT\b/YSKFMAT/ig;
    s/\bSTATE_VECTOR_4D\b/YSTATE_VECTOR_4D/ig;
    s/\bVAZX0\b/YVAZX0/ig;
    s/\bVAZG0\b/YVAZG0/ig;
    s/\bRSPFORCE\b/YSPFORCE/ig;
    $href->{statement}=$_;
  }
}
# =========================================================================
sub remake_arg_decl{
  my($statements,$prog_info) = @_;
  my($href,$content,@pu_args,$func,%tokens);
  my($left,$right,%arghash,$dim);
  our($nest_par,$name);

  my $dims='';
# Crack existing dummy declarations, build hash arghash
  foreach $href (@$statements) {
    last if($href->{prog_unit} >0);
    if($href->{content} eq 'SUBROUTINE') {   # Get arguments of subroutine
      $_=$href->{statement};
      my $dum=&parse_prog_unit(\$func,\@pu_args);
#      print Dumper(\@pu_args);
      for(@pu_args) {
	$_=uc($_);
	$arghash{$_}{other}='';
	$arghash{$_}{dimuse}=0;
	$arghash{$_}{intent}='';
	$arghash{$_}{used}=0;
	$arghash{$_}{set}=0;
	$arghash{$_}{reallyset}=0;
	$arghash{$_}{type}='';
	$arghash{$_}{comment}='';
	$arghash{$_}{inif}=0;
      }
      next;
    }
    if($href->{decl} == 2) {
      $_=$href->{statement};
      my $comment='';
      $comment=$1 if(/.*(\!.*)$/);
      s/\!.*\n/\n/g;                        # Remove trailing comments in all lines
      $_=uc($_);
      s/\s//g;
      if(/^(.+)::(.+)$/){
	$left=$1;
	$right=$2;
	$_=$right;
	s/$nest_par//g;
	s/($name)\*\w+/$1/g;
#	print "XX  $_ \n";
	foreach my $arg (@pu_args) {
	  if(/\b$arg\b/) {
#	    print "ARG $arg $left $_ \n";
	    $arghash{$arg}{linedec}=$href->{number};
	    $arghash{$arg}{comment}=$comment;
	    my @locdec =split ',',$left;
	    my $i=0;
	    foreach my $locdec (@locdec) {
	      if($i == 0) {
		$arghash{$arg}{type}=$locdec;
	      }
	      elsif($locdec=~/\bINTENT/) {
		$arghash{$arg}{intent}=','.$locdec;
	      }
	      else {
		$arghash{$arg}{other}=$arghash{$arg}{other}.','.$locdec;
	      }
	      $i++;
	    }
	    if($right=~/\b$arg\b(\*\w+)/) {
	      $dim=$1;
	    }
	    elsif($right=~/\b$arg\b($nest_par\*$nest_par)/) {
	      $dim=$1;
	    }
	    elsif($right=~/\b$arg\b($nest_par\*\w+)/) {
	      $dim=$1;
	    }
	    elsif($right=~/\b$arg\b(\*$nest_par)/) {
	      $dim=$1;
	    }
	    elsif($right=~/\b$arg\b($nest_par)/) {
	      $dim=$1;
	    }
	    else {
	      $dim='';
	    }
	    $arghash{$arg}{dim}=$dim;
	    $dims=$dims.$dim
	      }
	}
	foreach my $arg (@pu_args) {  # Is arg. used for dimensioning other args?
	  if($dims=~/\b$arg\b/i) {
	    $arghash{$arg}{dimuse}=1;
	  }
	}  
      }
    }
  }
  my $insert_line=0;
  foreach $href (@$statements) {
    last if($href->{prog_unit} >0);
    if($href->{decl} == 2 or $href->{content} eq 'PARAMETER') {                 
      $_=uc($href->{statement});
      next unless /\bPARAMETER\b/;
      my @tmpvar=/\b$name\b/g;
      foreach my $token (@tmpvar) {
	if($dims=~/\b$token\b/) {
	  $insert_line=$href->{number};
	}
      }
    }
  }
      
# Gather info to decide INTENT status
  my $inif=0;
  my @inif_stack=();
  my $cur_inif=0;
  foreach $href (@$statements) {
    last if($href->{prog_unit} >0);
    if($href->{exec}) {
      if($href->{content} eq 'ENDIF') {
	$inif--;
	$cur_inif=pop @inif_stack;
	next;
      }
      elsif($href->{content} eq 'ELSEIF' or $href->{content} eq 'ELSE') {
	$cur_inif=pop @inif_stack;
	$cur_inif=$href->{number};
	push @inif_stack,$cur_inif;
      }
      my ($left,$right);
      $_=$href->{statement};
      s/\!.*\n/\n/g;                        # Remove trailing comments in all lines
      my %setnow=();
      foreach my $arg (@pu_args) {
	$setnow{$arg}=0;
	$setnow{$arg}=1 if($arghash{$arg}{reallyset});
	unless ($setnow{$arg}) {
	  foreach my $xx (@inif_stack) {
	    $setnow{$arg}=1 if($xx == $arghash{$arg}{inif});
	  }
	}
      }
      
      if($href->{content} eq 'scal_assign' or $href->{content} eq 'array_assign') {
	s/\s//g;
	($left,$right)=/^(.+)=(.+)$/;
	$_=$right;
	foreach my $arg (@pu_args) {
	  if(/\b$arg\b/i) {
	    $arghash{$arg}{used}=1 unless $setnow{$arg};
	  }
	}
	$_=$left;
	if(/($nest_par)/) {
	  $_=$1;
	  foreach my $arg (@pu_args) {
	    if(/\b$arg\b/i) {
	      $arghash{$arg}{used}=1 unless $setnow{$arg};
	    }
	  }
	}
	$_=$left;
	foreach my $arg (@pu_args) {
	  if(/^$arg\b/i) {
	    $arghash{$arg}{set}=1;
	    $arghash{$arg}{inif}=$cur_inif;
	    $arghash{$arg}{reallyset}=1 unless($inif);
	  }
	}
      }
      elsif($href->{content} eq 'IF' ) {
	if($href->{content2} eq 'scal_assign' or $href->{content2} eq 'array_assign' or 
	   $href->{content2} eq 'CALL') {
	  s/\n//g;
	  ($left,$right)=/^\s*(IF\b\s*$nest_par)(.+)/i;
	  $_=$left;
	  foreach my $arg (@pu_args) {
	    if(/\b$arg\b/i) {
	      $arghash{$arg}{used}=1 unless $setnow{$arg};
	    }
	  }
	  $_=$right;
	  if($href->{content2} eq 'CALL') {
	    my $statement=$right;
	    my $inifx=1;
	    &propag_arg(\$statement,\%arghash,\$inifx,\%setnow);
	  }
	  else {
	    s/\s//g;
	    ($left,$right)=/^(.+)=(.+)$/;
	    $_=$right;
	    foreach my $arg (@pu_args) {
	      if(/\b$arg\b/i) {
		$arghash{$arg}{used}=1 unless $setnow{$arg};
	      }
	    }
	    $_=$left;
	    if(/($nest_par)/) {
	      $_=$1;
	      foreach my $arg (@pu_args) {
		if(/\b$arg\b/i) {
		  $arghash{$arg}{used}=1 unless $setnow{$arg};
		}
	      }
	    }
	    $_=$left;
	    foreach my $arg (@pu_args) {
	      if(/^$arg\b/i) {
		$arghash{$arg}{inif}=$cur_inif;
		$arghash{$arg}{set}=1;
	      }
	    }
	  }
	}
	else {
	  foreach my $arg (@pu_args) {
	    if(/\b$arg\b/i) {
	      $arghash{$arg}{used}=1 unless $setnow{$arg};
	    }
	  }
	}
      }
      elsif($href->{content} eq 'WHERE' ) {
	s/\s//g;
	($left,$right)=/^(WHERE$nest_par)(.+)/i;
	$_=$left;
	foreach my $arg (@pu_args) {
	  if(/\b$arg\b/i) {
	    $arghash{$arg}{used}=1 unless $setnow{$arg};
	  }
	}
	$_=$right;
	($left,$right)=/^(.+)=(.+)$/;
	$_=$right;
	foreach my $arg (@pu_args) {
	  if(/\b$arg\b/i) {
	    $arghash{$arg}{used}=1 unless $setnow{$arg};
	  }
	}
	$_=$left;
	foreach my $arg (@pu_args) {
	  if(/^$arg\b/i) {
	    $arghash{$arg}{inif}=$cur_inif;
	    $arghash{$arg}{set}=1;
	  }
	}
      }
      elsif($href->{content} eq 'CALL') {
	my $statement=$_;
	&propag_arg(\$statement,\%arghash,\$inif);
      }
      else{
	foreach my $arg (@pu_args) {
	  if(/\b$arg\b/i) {
	    $arghash{$arg}{used}=1 unless $setnow{$arg};
	  }
	}
      }
      if($href->{content} eq 'IF_construct') {
	$inif++;
	$cur_inif=$href->{number};
	push @inif_stack,$cur_inif;
      }
    }	  
  }

# Create INTENT statemant based on gathered info
  foreach my $arg (@pu_args) {
    if($arghash{$arg}{linedec}) {
      if($arghash{$arg}{nointent}) {
	unless($arghash{$arg}{intent}) {
	  $arghash{$arg}{intent}=' ';
	  $arghash{$arg}{comment}='! UNDETERMINED INTENT';
	}
      }
      else{
	my $intent='';
	$intent='IN' if($arghash{$arg}{used} or $arghash{$arg}{dimuse});
	$intent=$intent.'OUT' if($arghash{$arg}{set});
	if($intent) {
	  if($arghash{$arg}{intent} and $intent eq 'OUT') {
	    $intent='INOUT' if $arghash{$arg}{intent}=~/INOUT/i;
	  }
	  $arghash{$arg}{intent}=',INTENT('.$intent.')';
	}
	else {
	  $arghash{$arg}{intent}=' ';
	  $arghash{$arg}{comment}='! Argument NOT used';
	}
      }
    }
  }

# Remove existing argument declarations
  foreach my $arg (@pu_args) {
    if($arghash{$arg}{linedec}) {
      $_=$$statements[$arghash{$arg}{linedec}]->{statement};
      #   print "BEFORE $arg $_";
      if(/.*::\s*\b$arg\b\s*(\!.*\n)*$/i) {
	$_='';
      }
      elsif(/.*::\s*\b$arg\b\s*$nest_par\s*(\!.*\n)*$/i) {
	$_='';
      }
      elsif(/.*::\s*\b$arg\b\s*\*\s*\w+\s*(\!.*\n)*$/i) {
	$_='';
      }
      elsif(/.*::\s*\b$arg\b\s*\*\s*$nest_par\s*(\!.*\n)*$/i) {
	$_='';
      }
      elsif(/.*::\s*\b$arg\b\s*$nest_par\s*\*\s*\w+\s*(\!.*\n)*$/i) {
	$_='';
      }
      elsif(/.*::\s*\b$arg\b\s*$nest_par\s*\*\s*$nest_par\s*(\!.*\n)*$/i) {
	$_='';
      }
      else{
	/^(.*::)(.*)$/s;
	my $left=$1;
	$_=$2;
	s/\b$arg\b\s*$nest_par//i;
	s/\b$arg\b\s*\*\s*\w+//i;
	s/\b$arg\b\s*\*\s*$nest_par//i;
	s/\b$arg\b//i;
	s/,\s*,/,/;
	s/,(\s*)$/$1/;
	s/\n\s*\n/\n/g;
	$_=$left.$_;
	s/::\s*,/::/;
      }
 #   print "AFTER $arg $_\n";
      $$statements[$arghash{$arg}{linedec}]->{statement}=$_; 
    }
  }

 # Write out

  my $newdecl='';
  my $linedec;
  foreach my $arg (@pu_args) {
    if($arghash{$arg}{linedec}) {
      if($arghash{$arg}{other} and ! $arghash{$arg}{dim}) {
	$arghash{$arg}{other}=~s/\s//g;
	if($arghash{$arg}{other}=~/^,DIMENSION($nest_par)$/i) {
	  $arghash{$arg}{other}='';
	  $arghash{$arg}{dim}=$1;
	}
      }
      if($arghash{$arg}{dimuse}) { # Put declerations of args first
	$linedec=sprintf "%-18s%s%-14s%s%s%s%s %s",
	$arghash{$arg}{type},$arghash{$arg}{other},$arghash{$arg}{intent},
	    ' :: ',$arg,$arghash{$arg}{dim},$arghash{$arg}{comment},"\n";
	$newdecl=$newdecl.$linedec;
      }
    }
  }
  foreach my $arg (@pu_args) {
    if($arghash{$arg}{linedec}) {
      unless($arghash{$arg}{dimuse}) {
	$linedec=sprintf "%-18s%s%-14s%s%s%s %s%s",
	$arghash{$arg}{type},$arghash{$arg}{other},$arghash{$arg}{intent},
	    ' :: ',$arg,$arghash{$arg}{dim},$arghash{$arg}{comment},"\n";
	$newdecl=$newdecl.$linedec;
      }
    }
  }
#  print "INSERT_LINE $insert_line \n";
  if($insert_line) {
    $$statements[$insert_line]->{post_insert}=$newdecl;
  }
  else{
    foreach $href (@$statements) {
      if($href->{decl} == 2) {                 
	$href->{pre_insert}=$newdecl;
	last;
      }
    }
  }

#  print $newdecl;
#  print Dumper(\%arghash);
}

sub propag_arg{
  my ($statement,$arghash,$inif,$setnow) = @_;
  our ($name,$nest_par);
  my (%argpos);
  $_=$$statement;
  s/^\s*CALL\s+($name)//i;
  my $called=lc($1);
  s/\s//g;
  s/^\((.*)\)$/$1/s;
  my @inpars=/$nest_par/g;
  s/$nest_par//g;
  s/($name)%$name/$1/g;
  $_=uc($_);
#  print "PROPAG $called $_ ££ @inpars \n";
  my @call_args=split ',' , $_;
  my $i=0;
  my $interesting=0;
  %argpos=();
  foreach my $call (@call_args) {
    
#    print "CALL $called $call \n" ;
    if($call=~/(.+)=(.+)/) {
      $call=$2; #This just by-passes the problem
    }
    if(exists $$arghash{$call}) {
      if(exists $argpos{$call}) {
	push @{$argpos{$call}},$i;
      }
      else {
	my @i=($i);
	$argpos{$call}=[@i];
      }
      $interesting=1;
    }
    $i++;
  }
  if($interesting) {
    my $fname='/tmp/intblocks/'.$called.'.intfb.h';
    if( -f $fname ) {
      my @dumargs=();
      my $unit_name;
      print "FILE $fname FOUND \n";
      my @lines = &readfile($fname);
      my @loc_statements=(); 
      &expcont(\@lines,\@loc_statements);
      foreach my $href (@loc_statements) {
	$_=$href->{statement};
	if(/^\s*SUBROUTINE/i) {
	  my $dum=&parse_prog_unit(\$unit_name,\@dumargs);
	  next;
	}
	if(/::/) {
	  s/\s//g;
	  foreach my $arg (keys (%argpos)) {
	    my $set_before=$$setnow{$arg};
	    foreach my $i (@{$argpos{$arg}}){
	      if(/::$dumargs[$i]/) {
		if(/INTENT\(IN\)/i) {
		  $$arghash{$arg}{used}=1 unless $set_before;
		}
		elsif(/INTENT\(OUT\)/i) {
		  $$arghash{$arg}{set}=1;
		  $$setnow{$arg}=1 unless($$inif);
		}
		elsif(/INTENT\(INOUT\)/i) {
		  $$arghash{$arg}{set}=1;
		  $$arghash{$arg}{used}=1 unless $set_before;;
		  $$arghash{$arg}{reallyset}=1 unless($$inif);
		}
		elsif(/\! UNDETERMINED INTENT/) {
		  $$arghash{$arg}{nointent}=1;
		}
	      }
	    }
	  }
	}
      }
    }
    else {
      foreach my $arg (keys (%argpos)) {
	$$arghash{$arg}{nointent}=1;
      }
    }
  }
  for (@inpars) {
    foreach my $arg (keys (%$arghash)) {
      if(exists $$arghash{$arg}) {
	if(/\b$arg\b/i) {
	  $$arghash{$arg}{used}=1 unless $$setnow{$arg};
	}
      }
    }
  }
}
  
sub add_interface_blocks {
# Add interface block for called routines
  use File::Find;
  my($statements,$prog_info) = @_;
  my($href,$call);
  our($name,$nest_par);
  our(@call_names,@call_names_found,%call_names);

  return unless ($$prog_info{no_calls}); # Skip if there are no calls
  @call_names=();
  %call_names=();

  my $last_decl=0;
  my $in_intfblk=0;
  my %already_in=();
  ST:foreach $href (@$statements) {
    last if($href->{prog_unit} > 0);  # Only consider first program unit (no contains)
    if($href->{content} eq 'INTERFACE') {
      $in_intfblk=1;
      next;
    }
    if($href->{content} eq 'END INTERFACE') {
      $in_intfblk=0;
      next;
    }
    if($in_intfblk) {
      $_=$href->{statement};
      s/\#include\s*\"(\w+)\.h\"\s*$/$1/;
      $_=lc($_);
      $already_in{$_}++;
      next;
    }
    
# Find last declaration
    if($href->{decl}) {
      next if($href->{content} eq 'FORMAT');
      next if($href->{content} eq 'DATA');
      $last_decl = $href->{number} ;
    }
# Find calls
    next unless($href->{exec});
    if($href->{content} eq 'CALL' or 
       (exists  $href->{content2} and$ href->{content2} eq 'CALL') ) {
      $_=$href->{statement};
      /\s*\bCALL\b\s*($name)/i;
      my $call=lc($1);
      next if($already_in{$call}); # Exclude already existing interface block
      next if($call eq 'packmsg'); # A couple of special exceptions
      next if($call eq 'unpkmsg');
      $call_names{$call}++;
    }
  }
  

# Check that routine exists in IFS
  @call_names_found=();
  find(\&calls_wanted,'/tmp/27/ifs/');
#  find(\&calls_wanted,'/home/mats/work/cy28/ifs/');
#  find(\&calls_wanted,'/tmp/27/trans/');
  @call_names_found=sort(@call_names_found);
#  print "P2 @call_names_found \n";
  @call_names=@call_names_found;

# Contruct include block
  my $block='';
  for (@call_names) {
    $block=$block.'#include "'.$_.'.intfb.h"'."\n";
  }
#  print $block;

  my $clean=0;
  if(@call_names) {
    if($$prog_info{has_interface_block}) {
      foreach $href (@$statements) {
# Add interface block to routine that already has INTERFACE statement
	if($href->{content} eq 'END INTERFACE'){
	  if($href->{post_insert}) {
	    $href->{post_insert}=$href->{post_insert}."\n".$block;
	  }
	  else {
	    $href->{post_insert}="\n".$block;
	  }	    
	  last;
	}
      }
    }
# Add interface block to routine that does not have previous INTERFACE statement
    else {
      $href=@$statements[$last_decl];
      if($href->{post_insert}) {
	$href->{post_insert}=$href->{post_insert}."\n".$block;
      }
      else {
	$href->{post_insert}="\n".$block;
      }	    
    }
# Remove from EXTERNAL statement where interface block has been added
    foreach $href (@$statements) {
      if($href->{content} eq 'EXTERNAL') {
	$_=$href->{statement};
	foreach my $ext (@call_names) {
	  s/\b$ext\b//i;
	}
	s/,\s*,/,/g;
	s/^(\s*EXTERNAL\s*),/$1/i;
	s/^(\s*EXTERNAL.*),\s*$/$1/i;
	s/^\s*EXTERNAL\s*,*\s*$//i;
	$href->{statement}=$_;	
      }
    }
  }
}
#======================================================================================
sub calls_wanted {
  # Used by Find as called from add_interface_blocks
  our(%call_names,@call_names_found);
  return unless (/^(\w+)\.F90$/);
  my $call=$1;
  if($call_names{$call}) {
    push(@call_names_found,$call);
  }    
}
sub remove_some_comments{
  my($statements) = @_;
  my $prev_empty=0;
  foreach my $href (@$statements) {
    if($href->{content} eq 'comment'){
      $_=$href->{statement};
      if(/^\s*$/) {
	if($prev_empty) {
	  s/\s*//;
	  $href->{statement}=$_;
	}
	else {
	  $prev_empty=1;
	} 
	next;
      }
      $prev_empty=0;
      s/^\s*![\s\*]*\bLOCAL\s+(INTEGER|REAL|LOGICAL|CHARACTER)\s+(SCALARS|ARRAYS).*\n$//i;
      s/^\s*![\s\*]*\bDUMMY\s+(INTEGER|REAL|LOGICAL|CHARACTER)\s+(SCALARS|ARRAYS).*\n$//i;
      s/^\s*![\s\*]*\bLOCAL\s+(INTEGER|REAL|LOGICAL|CHARACTER).*\n$//i;
      s/^\s*![\s\*]*\bDUMMY\b\s*$//i;
      s/^\s*![\s\*]*\bLOCAL\b\s*$//i;
      s/^\s*![\s\*]*\bLOCAL\b:\s*$//i;
      s/^\s*![\s\*]*\bLOCAL ARRAYS\b[\s\*]*$//i;
      s/^\s*![\s\*]*\bLOCAL SCALARS\b\s*$//i;
      s/^\s*![\s\*]*\s*\d\.\d+\s*\bLOCAL ARRAYS\b\s*$//i;
      s/^\s*![\s\*]*\s*=== LOCAL ARRAYS ===\s*$//i;
      $href->{statement}=$_;
    }
    else {
      $prev_empty=0;
    }
  }
}      
sub get_calls_inc {
  my($statements,$calls,$intfb) = @_;
  foreach my $href (@$statements) {
    if($href->{content} eq 'CALL') {
      $_=$href->{statement};
      /^\s*CALL\s+([A-Z]\w*)/i;
      $$calls{lc($1)}++;
    }
    elsif($href->{content} eq 'IF') {
      if($href->{content2} eq 'CALL') {
	$_=$href->{statement};
	/\bCALL\s+([A-Z]\w*)/i;
	$$calls{lc($1)}++;
      }
    }
    elsif($href->{content} eq 'include') {
      $_=$href->{statement};
      $$intfb{$1}=1 if(/["](\S+)\.intfb\.h["]/);
      $$intfb{$1}=2 if(/["](\S+)\.h["]/); # For old-style interface blocks
    }
  }
}

1;
      
__END__
