# Take a list of files and for each file generate a line of code containing
# its dependencies in a form suitable for inclusion into a make file.
# Write output to STDOUT.

if ( @ARGV != 0 ) {
  print "Usage: makdep\n";
  exit;
}

if ( open(FILEPATH,"< Filepath") ) {
  @paths = <FILEPATH>;
} else {
  @paths = ".\n";
}
open(SRCFILES,"< Srcfiles") || die "Can't open Srcfiles: $!\n";
open(OBJECTS,"> Objects") || die "Can't open Objects\n";
open(DEPENDS,"> Depends") || die "Can't open Depends\n";

# Make list of paths to use when looking for files.  Remove newline characters.
# Prepend "." so search starts in current directory.  This default is for
# consistency with the way GNU Make searches for dependencies.

chop @paths;
unshift(@paths,'.');
foreach $dir (@paths) {
  $dir = `/bin/csh -cf "glob $dir"`;     # Expand tildes in path names.
}

# Make list of files containing source code.

@src = <SRCFILES>;
chop @src;

# Write dependencies formatted for inclusion into a make file to STDOUT.

$count = 0;
print OBJECTS "OBJS = ";
foreach $dotf ( @src ) {
  if ( $count != 0 && $count % 4 == 0 ) {
    print OBJECTS "\\\n\t";
  }
  $count++;
  $doto = $dotf;
  $doto =~ s/\.F$/\.o/;
  $doto =~ s/\.c$/\.o/;
  print OBJECTS "$doto\t";
}
print OBJECTS "\n";
    
FILE:

foreach $file ( @src ) {

  local( $list, $x, $y, $head, @dep );

  @dep = (); # make @dep the null list

  @list = &dependents( $file );

  # Check the list returned from dependents for error flags and remove
  # redundant dependencies.

  LIST_MEMBER:
  for ( $i = 0; $i <= $#list; ++$i ) {
    $x = $list[$i];
    if ( $x == -1 ) {                            # error return code
      if ( $file eq $list[$i+1] ) {
	print STDERR "$file not found\n";
	next FILE;
      } else {
	++$i;
#	print STDERR "$file dependency $list[$i] not found\n";
	# Remove dependency not found from the dependency list
        # (or make will try to create it!).
	# It is last one in @dep since it was added to the list before the
	# function "dependents" tried to find it.
	pop( @dep );
	next LIST_MEMBER;
      }
    } else {        # add filename to dependency list - check for duplication.
      foreach $y ( @dep ) {
	if ( $y eq $x ) { 
	  next LIST_MEMBER;
	}
      }
      push( @dep, $x );
    }
  }

  # No errors were encountered... format the list for inclusion in makefile.

  $file =~ /\s*(\w[^.]*)/;
  $head = "$1.o";  # The form of $head is determined by how the
                            # archive members are specified in the makefile.

#  print "$file : @dep\n";
  print DEPENDS "$head : $file @dep\n";

}

sub findsrc {

# Search for the specified file in the list of directories in the global
# array @path.  Return the first occurance found, or the null string if
# the file is not found.

  local( $file ) = @_;
  local( $dir, $fname );

  foreach $dir (@paths) {

    if( $dir =~ m#/$# ) {             # allow directory name to end with /
      $fname = $dir . $file;
    } else {
      $fname = $dir . '/' . $file;
    }

    if ( -e  $fname ) {
      return $fname;
    }

  }
  return '';  # file not found
}

sub dependents {

  # Search recursively for all files that are "#include"d by the cpp
  # preprocessor.  

  local( $file ) = @_;
  local( @out, $fh );

  # Find the file.

  if( ! ($absname = &findsrc( $file )) ) {
    return -1, $file; # file not found
  }

  # Make a unique filehandle.

  $fh = $file . 'FH';
  $fh =~ tr/a-z/A-Z/;

  open( $fh, $absname );

  # Search for "#include" and strip filename when found.  Append the file to
  # the output array and descend into that file to continue the search.

  while ( <$fh> ) {
    if ( /^#include\s+[<"](.*)[>"]/ ) {
      push( @out, $1 );
      push( @out, &dependents( $1 ) );
    }
  }
  close( $fh );
  return @out;
}
