#!/usr/local/bin/perl

# CONFIGURE -- Configure cdbs for running on a specific machine
#
# Usage: configure cmd [cmd ...] 
# where cmd is one of 
# source|version|defines|includes|cgibin|scripts|topmake|make

use Cwd;
use File::Copy;
use File::Find qw(finddepth);
*filename = *File::Find::name;

my ($job, $date);
my $back = &goto_top;
%config =  &configure;

foreach $job (@ARGV) {
    if ($job eq 'source') {
	chdir ('src');
	&remove_source;
	chdir ('..');

    } elsif ($job eq 'version') {
	$date = localtime;
	$date =~ s/\d+:\d+:\d+\s+//;

	update_file ('include/system.h', '\#define\s+VERSION_STRING', 
	"\#define  VERSION_STRING\t\"$date\"\t/* Update each release !! */\n");

    } elsif ($job eq 'defines') {
	update_file ('util/defines.sh', '^CDBS_TOP=', 
		     "CDBS_TOP=$config{CDBS_TOP}/\n");

	update_file ('util/defines.csh', '^setenv CDBS_TOP', 
		     "setenv CDBS_TOP $config{CDBS_TOP}/\n");

    } elsif ($job eq 'includes') {
	update_file ('include/system.h', '\#define\s+CDBSDIR', 
		     "\#define  CDBSDIR\t\"$config{CDBSDIR}\"\n");

	update_file ('include/system.h', '\#define\s+CGIURL', 
		     "\#define  CGIURL\t\t\"$config{CGIURL}\"\n");

	update_file ('include/system.h', '\#define\s+DATADIR', 
		     "\#define  DATADIR\t\"$config{CDBS_TOP}/data/\"\n");

    } elsif ($job eq 'cgibin') {
	update_file ('util/getref.cgi', '^CDBS_TOP=', 
		     "CDBS_TOP=$config{CDBS_TOP}/\n");

	update_file ('util/query.cgi', '^CDBS_TOP=', 
		     "CDBS_TOP=$config{CDBS_TOP}/\n");

	if ($config{USECGI} eq 'yes') {
	    copy ('util/getref.cgi', "$config{CGIDIR}getref.cgi") or
	      warn "Couldn't copy getref.cgi: $!\n";

	    copy ('util/query.cgi', "$config{CGIDIR}query.cgi") or
	      warn "Couldn't copy query.cgi: $!\n";
	}

    } elsif ($job eq 'scripts') {
	if (-f 'src/loopfits/loopfits.pl') {
	    update_file ('src/loopfits/loopfits.pl', '^\$tables_bin =', 
		     "\$tables_bin = '$config{TABLES_BIN}';\n");

	    chmod 0755, 'src/loopfits/loopfits.pl'; 
	}

	update_file ('util/rungetref.sh', '^CDBS_TOP=', 
		     "CDBS_TOP=$config{CDBS_TOP}/\n");

	update_file ('util/rungetref.sh', '^SDAS_BIN=', 
		     "SDAS_BIN=$config{SDAS_BIN}\n");

	chmod 0755, 'util/rungetref.sh';

    } elsif ($job eq 'topmake') {
	chdir ('src');
	&update_top_make;
	chdir ('..');

    } elsif ($job eq 'make') {
	chdir ($back);
	&update_task_make;
	&goto_top;

    } else {
	warn "Unrecognized command ignored: $job\n";
    }
}

#----------------------------------------------------------------------
# Break a variable at whitespace so it will fit on a line

sub breakline {
    local ($line, $firstlen, $restlen) = @_;
    local ($lastbrk, $lastsp, $nextsp, $maxlen);
 
    $nextsp = 0;
    $maxlen = $firstlen;
    $lastbrk = $lastsp = $nextsp - 1;
    while (($nextsp = index ($line, ' ',  $nextsp)) >= 0) {
        if ($nextsp - $lastbrk > $maxlen && $lastsp >= 0) {
            substr ($line, $lastsp, 1) = "\n";
            $lastbrk = $lastsp;
	    $maxlen = $restlen;
        }
 
        $lastsp = $nextsp ++;
    }
 
    substr ($line, $lastsp, 1) = "\n" if length ($line) - $lastbrk > $maxlen &&
	$lastsp > 0;

    split (/\n/, $line);
}

#----------------------------------------------------------------------
# Read a configuration file into a hash

sub configure {
    my ($uname, $machine, $line, %config);

    $uname = `uname -n`;
    chomp $uname;
    ($machine = $uname) =~ s/\..*//;

    open (CONFIG, "util/$machine.cfg") or die "No Config file for $machine\n";

    while (<CONFIG>) {
	chomp;
	$line .= $_;
	next if $line =~ s/\\$/ /;

	$line =~ s/\#.*//;
	$line =~ s/^\s+//;
	$line =~ s/\s+$//;
	next unless length ($line);

	($name, $value) = split (/\s*=\s*/, $line, 2);
	$config{$name} = $value;
	$line = '';
    }

    $config{MACHINE} = $machine;
    $config{CDBS_TOP} = cwd ();
    return %config;
}

#----------------------------------------------------------------------
# Evaluate a hash entry containing hash file like macros.

sub eval_macro {
    my ($value) = @_;

    while ($value =~ s/\$\(([^\)]*)\)/$config{$1}/ge) {
	1;
    }

    return $value;
}

#----------------------------------------------------------------------
#Change directories to the top level CDBS directory

sub goto_top {
    my ($dir, $back, @dir);

    $back = cwd ();
    @dir = split (/\/+/, $back);
    pop (@dir);

    while ($dir = pop (@dir)) {
	last unless chdir ('..');
	return $back if -d 'util' && -d 'src' && -d 'include';
    }

    die "Can't find top level CDBS directory from $back";
}

#----------------------------------------------------------------------
# Replace variable tokens in a Makefile pattern

sub fill_pattern {
    my ($pattern, $output, %var) = @_;
    my $text;
    local $/;

    unless (open (PAT, "$pattern")) {
	warn "Can't read pattern file $pattern: $!\n";
	return;
    }

    $text = <PAT>;
    close (PAT);

    $text =~ s/\#\#([^\#]+)\#\#/exists($var{$1}) ? $var{$1} : ''/gse;

    if (-f $output) {
	unless (rename ($output, "$output.bak")) {
	    warn "Can't rename $output: $!\n";
	    return;
	}
    }

    unless (open (OUT, ">$output")) {
	warn "Can't create $output: $!\n";
	return;
    }

    print OUT $text;
    close (OUT);

    unlink ("$output.bak") if -f "$output.bak";
}

#----------------------------------------------------------------------
# Remove a file or empty directory. Called by File::Find

sub remove_file {
    return if $_ eq '.' || $_ eq '..';

    if (! -l && -d _) {
	rmdir or warn "Couldn't rmdir $filename: $!\n";
    } else {
	unlink or warn "Couldn't rm $filename: $!\n";
    }
}

#----------------------------------------------------------------------
# Remove source directories not needed on this machine

sub remove_source {
    my (%keep, $dir);

    foreach $dir (split (/\s+/, $config{LIBRARIES})) {
	$keep{$dir} = 1;
    }

    foreach $dir (split (/\s+/, $config{TASKS})) {
	$keep{$dir} = 1;
    }

    foreach $dir (split (/\s+/, $config{SOURCES})) {
	next if $keep{$dir} || ! -e $dir;
	finddepth (\&remove_file, $dir);
	rmdir ($dir) or warn "Couldn't rmdir $dir: $!\n";
    }

}

#----------------------------------------------------------------------
# Update line in a file which matches a pattern

sub update_file {
    my ($file, $pattern, $newline) = @_;
    my ($done, $status);

    $status = rename ($file, "$file.bak") and
        open (OLDFILE, "$file.bak") and
	open (NEWFILE, ">$file");

    unless ($status) {
	warn  "WARNING Cannot update $file: $!\n";
	return;
    }
    
    while (<OLDFILE>) {
	if (/$pattern/) {
	    if ($done) {
		print NEWFILE $_;
		warn "More than one line matches '$pattern' in $file\n",

	    } else {
		print NEWFILE $newline;
		$done = 1;
	    }

	} else {
	    print NEWFILE $_;
	}
    }

    warn  "No changes made to $file\n" unless $done;

    close (OLDFILE);
    close (NEWFILE);
    unlink "$file.bak";
}

#----------------------------------------------------------------------
# Create a new Makefile for a  task

sub update_task_make {
    my ($name, $value, $pattern, $src, $obj, @names, %depend, %var);

    $includes = eval_macro ($config{INC});

    unless (open (INPUT, "cc -xM1 $includes *.c 2>/dev/null |")) {
	warn "Can't run cc -xM1\n";
	return;
    }

    while (<INPUT>) {
	chop;
	tr/ //d;
	($name, $value) = split (/:/);
    
	next if $name eq 'main.o';
	next if $value =~ m%^/%;
    
	$value =~ s%//%/%g;
    
	$pattern = $value;
	$pattern =~ s/(\W)/\\\1/g;
	next if $depend{$name} =~ /$pattern/;
    
	if ($depend{$name}) {
	    $depend{$name} .= " $value";
	} else {
	    $depend{$name} = $value;
	}
    }

    close (INPUT);

    @names = sort (keys (%depend));
    $var{LIB} = join (" \\\n\t", map ("\$(LIB)($_)", @names));

    foreach $obj (@names) {
	($src = $obj) =~ s/\.o$/\.c/;
	$var{CC} .= "\$(LIB)($obj) :  ";
	$var{CC} .= join (" \\\n\t", breakline ($depend{$obj}, 35, 65)) . 
	  "\n";
	$var{CC} .= "\t\$(CC) \$(CFLAGS) -c -o $obj $src\n";
	$var{CC} .= "\tar -rv \$(LIB) $obj\n";
	$var{CC} .= "\trm -f $obj\n\n";
    }
		      
    fill_pattern ('Makefile.pat', 'Makefile', %var);
}

#----------------------------------------------------------------------
# Create a new top level Makefile

sub update_top_make {
    my (%var, $dir);

    unlink 'Makefile.inc';
    link "../util/$config{MACHINE}.cfg", 'Makefile.inc' 
      or warn "Cannot link Makefile.inc: $!\n";

    foreach $dir (split (/\s+/, $config{LIBRARIES})) {
	$var{ALL} .= "\tcd $dir; make all\n";
	$var{MAKE} .= "\tcd $dir; make make\n";
    }

    foreach $dir (split (/\s+/, $config{TASKS})) {
	$var{ALL} .= "\tcd $dir; make all\n";
	$var{MAKE} .= "\tcd $dir; make make\n";
    }

    fill_pattern ('Makefile.pat', 'Makefile', %var);
}

