#!/usr/bin/perl -w
#
# $Id$ 
#


sub which {
    my ($cmd, $found, $warning);
    $cmd = $_[0]; 
    $pw = $_[1]; # do we print a warning or not?
    open WHICH, "which $cmd 2>&1 |" or die "can't fork which $cmd";
    while (<WHICH>) {
	if (/which: no $cmd/ || /^no $cmd/ || /^$cmd: Command not found/) { 
	    if ($pw) {
		$warning = "--> WARNING: No $cmd in current path.\n";
		print "\n$warning"; 
		$errorstr .= "$warning";
		$errors = 1;
	    }
	    $found = 0;
	} else {
	    print "\t$_";
	    $found = 1;
	}
    }
    close WHICH;
    return $found;
}

sub is_windows() {
    return 1 if (grep { /cygwin/i } `uname`);
    return 0;
}

sub chk_uisp() {
    my $found;
    my $versionok = 0;
    print "uisp:\n";
    $found = which("uisp", 1);
    if ($found) {
	open UISP, "uisp --version 2>&1 |" or die "can't fork uisp --version";
	while (<UISP>) {
	    if (/version/) {
		print "\t$_";
		$versionok = 1 if /20030820tinyos/;
	    }
	}
	close UISP;
	if (!$versionok) {
	    $warning = "--> WARNING: The uisp version found by toscheck is not '20030820tinyos'. " .
		"Please update your uisp version. The source for uisp version 20030820tinyos " .
		"can be found in the TinyOS 1.1.0 distribution.\n";
	    print "\n$warning";
	    $errorstr .= $warning;
	    $errors = 1;
	}
    } else {
	$warning = "--> WARNING: toscheck couldn't find the uisp program. Uisp is used to " . 
	    "program the motes. Please install uisp version 20030820tinyos which can be found " .
	    " in the TinyOS 1.1.0 distribution.\n";
	
	print "\n$warning";
	$errorstr .= $warning;
	$errors = 1;
    }
    print "\n";
}

sub chk_cygwin() {
    my $system;
    return if !is_windows();
    print "Cygwin:\n";
    open CYGCHECK, "cygcheck -s 2>&1 |" or die "can't fork cygcheck -s";
    while (<CYGCHECK>) {
	print "\t$_";;
    }
    print "\n";
}

#
# Look for the phrase 'version 1.4' in the first line
#
sub chk_java() {
    my $found;
    my $versionok = 0;
    print "java:\n";
    $found = which("java", 1);
    if ($found) {
	open JAVA, "java -version 2>&1 |" or die "can't fork java -version";
	while (<JAVA>) {
	    if ($_ =~ /version \"1\.4/) {
		print "\t$_";
		$versionok = 1;
	    }
	}
	close JAVA;
	if (!$versionok) {
	    $warning = "--> WARNING: The JAVA version found first by toscheck may not be version 1.4 " .
		"which is required by TOS. Please " .
		"ensure that the located Java version is 1.4\n";
	    if (is_windows()) {
		$warning .= "Depending on your PATH environment variable, there is often a 1.2 " .
		    " version of java.exe in c:\\windows\\system32 that is " .
		    "\"seen\" first. Check that this is version 1.4 or reconfigure your PATH " .
		    "environment variable if this is the case.\n";
	    }
	    print "\n$warning";
	    $errorstr .= $warning;
	    $errors = 1;
	}

    } else {
	$errors = 1;
    }
    print "\n";
};

sub chk_perl() {
    my $found;
    print "perl:\n";
    $found = which("perl", 1);
    if ($found) {
	print "\tVersion: ";
	open PERL, "perl --version 2>&1 |" or die "can't fork perl --version";
	while (<PERL>) {
	    print $1 if /(v[\d|\.]+.*$)/;
	}
	close PERL;
    } else {
	$errors = 1;
    }
    print "\n\n";
};

sub chk_lex {
    my $found;
    print "flex:\n";
    which("flex", 1);
    print "\n";
}

sub chk_yacc {
    my $found;
    print "bison:\n";
    which("bison", 1);
    print "\n";
}

sub chk_nesc {
    my $found;
    print "nesc:\n";
    $found = which("ncc", 1);
    if ($found) {
	print "\tVersion: ";
	open NESC, "ncc --version 2>&1 |" or die "can't fork ncc --version";
	while (<NESC>) {
	    if (/ncc:/) {
		print $_;
		$versionok = 1 if /1\.1/;
	    } elsif (/Unknown target mica/) {
		$warning = "--> WARNING: ncc (nesc) was found, but the version could " .
		    "not be verified. Verify that the ncc version that you have is 1.1 " .
		    "by running ncc --version. If you get an error regarding platforms, " .
		    "please see the TinyOS FAQ for help: www.tinyos.net/faq.html\n";
		print "\n$warning";
		$errorstr .= $warning;
		$errors = 1;
	    }
	}
	close NESC;
	if (!$versionok) {
	    $warning = "--> WARNING: The ncc found by toscheck is not version 1.1. " .
		"Please update your nesc version to 1.1 tinyos.\n";
	    print "\n$warning";
	    $errorstr .= $warning;
	    $errors = 1;
	}
    } else {
	my $warning = "--> WARNING: ncc not found. Please install nesc 1.1.\n";
	print "\n$warning";
	$errorstr .= $warning;
	$errors = 1;
    }
    print "\n\n";
}

sub chk_avrgcc {
    my $found;
    print "avr-gcc:\n";
    $found = which("avr-gcc", 1);
    if ($found) {
	print "\tVersion: ";
	open AVRGCC, "avr-gcc --version 2>&1 |" or die "can't fork avr-gcc --version";
	while (<AVRGCC>) {
	    if (/avr-gcc/) {
		print "$_";
		$versionok = 1 if /3\.3-tinyos/;
	    }
	}
	close AVRGCC;
	if (!$versionok) {
	    $warning = "--> WARNING: The avr-gcc found by toscheck is not 3.3-tinyos. " .
		"Please update your avr-gcc compiler to 3.3-tinyos.\n";
	    print "\n$warning";
	    $errorstr .= $warning;
	    $errors = 1;
	}    

    } else {
	my $warning = "--> WARNING: avr-gcc not found.\n";
	print "\n$warning";
	$errorstr .= $warning;
	$errors = 1;
    }
    print "\n\n";
}

sub chk_path {
    my @dirs;
    print "Path:\n";
    if (exists $ENV{PATH}) {
	@dirs = split /:/, $ENV{PATH};
	foreach $dir (@dirs) {
	    print "\t$dir\n"
	}
	
    } else {
	my $warning = "--> WARNING: PATH environment variable doesn't exist.\n";
	print "\n$warning";
	$errorstr .= $warning;
	$errors = 1;
    }
    print "\n";
}

#
# - should include tinyos-1.x/tools/java
# - '.' is recommended
# 
sub chk_classpath {
    my @dirs;
    my $warning; 
    my $toolsfound = 0;
    my $commfound = 0;
    my $dotfound = 0;
    print "Classpath:\n";
    if (exists $ENV{CLASSPATH}) {
	if (is_windows()) {
	    $separator = ';';
	} else {
	    $separator = ':';
	}
	@dirs = split /$separator/, $ENV{CLASSPATH};
	foreach $dir (@dirs) {
	    print "\t$dir\n";
	    if ($dir =~ /tinyos-1\.x[\\\/]tools[\\\/]java/) {
		$toolsfound = 1;
	    }
	    if ($dir =~ /^\.$/) {
		$dotfound = 1;
	    }

	}
	print "\n";
	if ($toolsfound == 0) {
	    $warning = "--> WARNING: CLASSPATH may not include {TOSROOT}/tinyos-1.x/tools/java. Please add " .
		          "the {TOSROOT}/tinyos-1.x/tools/java directory to your CLASSPATH or you may " .
			      "experience configuration problems\n";
	    print "$warning";
	    $errorstr .= $warning;
	    $errors = 1;
	}
	if ($dotfound == 0) {
	    $warning = "--> WARNING: CLASSPATH may not include '.' (that is, " .
		          " the symbol for the current working directory). Please add " .
		              "'.' to your CLASSPATH or you may " .
			          "experience configuration problems.\n";
	    print "$warning";
	    $errorstr .= $warning;
	    $errors = 1;
	}
    } else {
	my $warning = "--> WARNING: CLASSPATH environment variable doesn't exist.\n";
	print "$warning";
	$errorstr .= $warning;
	$errors = 1;
    }
    print "\n\n";
}

# List the rpms 
sub chk_rpms {
    my $found;
    print "rpms:\n";
    $found = which("rpm", 0);
    if ($found) {
	open RPM, "rpm -qa 2>&1 |" or die "can't fork rpm";
	while (<RPM>) {
	    if (/avr/ || /tinyos/ || /nesc/ || /avarice/) {
		print "\t$_";
	    }
	}
    }
    print "\n\n";
}

sub chk_graphviz {
    my $found;
    my $versionok = 0;
    print "graphviz:\n";
    $found = which("dot", 1);
    if ($found) {
	open GRAPHVIZ, "dot -V 2>&1 |" or die "can't fork dot -V to check graphviz";
	while (<GRAPHVIZ>) {
	    if (/version/) {
		print "\t$_";
		$versionok = 1 if /1\.10/;
	    }
	}
	close GRAPHVIZ;
	if (!$versionok) {
	    $warning = "--> WARNING: The graphviz (dot) version found by toscheck is not 1.10. " .
		"Please update your graphviz version if you'd like to use the nescdoc " .
		"documentation generator.\n";
	    print "\n$warning";
	    $errorstr .= $warning;
	    $errors = 1;
	}
    } else {
	$warning = "--> WARNING: toscheck could not find the 'dot' executable which is part " .
	    "of the AT&T Graphviz package. Please install version 1.1.0 of Graphviz if you'd " .
	    " like to use the nescdoc documentation generator.\n";
	print "\n$warning";
	$errorstr .= $warning;
	$errors = 1;
    }
    print "\n";
}

sub chk_avarice {
    my $found;
    my $versionok = 0;
    print "avarice:\n";
    $found = which("avarice", 0);
    if ($found) {
	open AVARICE, "avarice --version 2>&1 |" or die "can't fork avarice --version";
	while (<AVARICE>) {
	    if (/version/) {
		print "\t$_";
		$versionok = 1 if /2\.0\.20030825cvs/;
	    }
	}
	close AVARICE;
	if (!$versionok) {
	    $warning = "--> WARNING: The avarice version found by toscheck is not 2.0.20030806cvs " .
		"Please update your avarice version.";
	print "\n$warning";
	$errorstr .= $warning;
	$errors = 1;
	}
    }
    print "\n";
}

sub chk_avras {
    my $found = 0;
    my $versionok = 0;
    print "avr-as:\n";
    $found = which("avr-as", 1);
    if ($found) {
	open AVRAS, "avr-as --version 2>&1 |" or die "can't fork avr-as --version";
	while (<AVRAS>) {
	    if (/GNU assembler/) {
		print "\t$_";
		$versionok = 1 if /2\.13\.2\.1/;
	    }
	}
	close AVRAS;
	if (!$versionok) {
	    $warning = "--> WARNING: The avr-as version found by toscheck is not 2.13.2.1 " .
		"Please update your avr-as version by updating your avr-binutils package.";
	print "\n$warning";
	$errorstr .= $warning;
	$errors = 1;
	}
    } else {
	my $warning = "--> WARNING: Couldn't find avr-as. Please install avr-binutils " .
	    "version 2.13.2.1 \n";
	print "$warning";
	$errorstr .= $warning;
	$errors = 1;
    }
    print "\n";
}

sub chk_avrgdb {
    my $found;
    my $versionok = 0;
    print "avr-gdb:\n";
    $found = which("avr-gdb", 0);
    if ($found) {
	open AVRGDB, "avr-gdb --version 2>&1 |" or die "can't fork avr-gdb --version";
	while (<AVRGDB>) {
	    if (/GNU gdb/) {
		print "\t$_";
		$versionok = 1 if /cvs-pre6\.0-tinyos/;
	    }
	}
	close AVRGDB;
	if (!$versionok) {
	    $warning = "--> WARNING: The avr-gdb version found by toscheck is not cvs-pre6.0-tinyos. " .
		"Please update your avr-gdb version.";
	print "\n$warning";
	$errorstr .= $warning;
	$errors = 1;
	}
    }
    print "\n";
}

sub chk_javacomm {
    print "javax.comm:\n";
    $ok = open TMP, ">testcomm.java";
    $ok = $ok && print TMP "class Test { javax.comm.CommPortIdentifier x; }";
    $ok = $ok && close TMP;
    $ok = $ok && open JAVAC, "javac testcomm.java 2>&1 |";
    @result = <JAVAC> if $ok;
    $ok = $ok && close JAVAC;
    unlink "testcomm.java";

    if (!$ok || join('', @result) =~ /error/) {
	$warning = "--> WARNING: Could not find the javax.comm classes.\n" .
	    "Please ensure the java Comm API is installed correctly.\n";
	print "$warning";
	if ($ok) {
	    print "Compiler output was:\n";
	    print @result;
	    print "\n";
	}
	else {
	    print "Couldn't invoke javac on test program\n";
	}
	$errorstr .= $warning;
	$errors = 1;
    }
    else {
	print "\tjavax.comm ok\n"
    }
    print "\n";
}

$errorstr = "";
$errors = 0;    # binary, not a counting var #

chdir "/tmp";

chk_path();
chk_classpath();
chk_rpms();
chk_nesc();
chk_avrgcc();
chk_perl();
chk_lex();
chk_yacc();
chk_java();
chk_javacomm();
chk_cygwin();
chk_uisp();
chk_graphviz();
chk_avras();
chk_avarice();
chk_avrgdb();

if ($errors) {
    print "\ntoscheck completed with errors:\n\n$errorstr\n";
} else {
    print "\ntoscheck completed without error.\n\n";
}

__END__
