#!/perl -w
# NAME: findinpath.pl
# AIM: Given an input file, find it in the PATH enviroment variable ...
# 04/05/2013 - Allow additional paths to be searched
# 19/10/2011 - show the number of the PATH found in if 1 and exact
# 14/07/2010 - checkout, and add 'potential' finds, like '*item*'...
# 05/05/2008 - geoff mclane - http://geoffair.net/mperl
use strict;
use warnings;
use File::stat; # to get the file date and size
use Env qw(PATH);	# for reading PATH environment variable
unshift(@INC, 'C:/GTools/perl');
#require 'logfile.pl' or die "Unable to load logfile.pl ...\n";
# log file stuff
#my ($LF);
my $pgmname = $0;
if ($pgmname =~ /\w{1}:\\.*/) {
	my @tmpsp = split(/\\/,$pgmname);
	$pgmname = $tmpsp[-1];
}
#my $outfile = "temp.$pgmname.txt";
#open_log($outfile);

my $inp_file = '';
#my $inp_file = 'java.exe';
#my $inp_file = 'Perl58.dll';

my $CHKPATH = $ENV{PATH};
#prt( "Check PATH=$CHKPATH ...\n" );
my @patharr = split(';', $CHKPATH);
my @pathfnd = ();
my @potential = ();
my $pcnt = scalar @patharr;
my $verbosity = 0;
my $debug = 0; # set to run in editplus
my $def_inf = 'java';
my $min_size = 12;
my @user_paths = ();
my $recursive = 0;
my @warnings = ();

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

sub in_file_has_wild($) {
   my ($inf) = @_;
   if ($inf =~ /(\*|\?)/) {
      return 1;
   }
   return 0;
}

sub prt($) {
	my ($txt) = shift;
	print $txt;
}

sub mydie($) {
	my ($txt) = shift;
	die $txt;
}

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        prt( "\nNo warnings issued.\n\n" ) if (VERB9());
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
#    close_log($outfile,$load_log);
    exit($val);
}


sub prtw($) {
   my ($tx) = shift;
   $tx =~ s/\n$//;
   prt("$tx\n");
   push(@warnings,$tx);
}

sub strip_quotes($) {
	my ($txt) = shift;
	if (substr($txt,0,1) eq '"') {
		$txt = substr($txt,1);
		if (substr($txt,-1,1) eq '"') {
			$txt = substr($txt,0,length($txt)-1);
		}
	}
	return $txt;
}

# My particular time 'translation' - replaced date_string
sub YYYYMMDD($) {
	#  0    1    2     3     4    5     6     7     8
	my ($tm) = shift;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($tm);
	$year += 1900;
	$mon += 1;
	my $ymd = "$year/";
	if ($mon < 10) {
		$ymd .= '0'.$mon.'/';
	} else {
		$ymd .= "$mon/";
	}
	if ($mday < 10) {
		$ymd .= '0'.$mday;
	} else {
		$ymd .= "$mday";
	}
	return $ymd;
}

# My particular 'nice number'
sub get_nn($) { # perl nice number nicenum add commas
	my ($n) = shift;
	if (length($n) > 3) {
		my $mod = length($n) % 3;
		my $ret = (($mod > 0) ? substr( $n, 0, $mod ) : '');
		my $mx = int( length($n) / 3 );
		for (my $i = 0; $i < $mx; $i++ ) {
			if (($mod == 0) && ($i == 0)) {
				$ret .= substr( $n, ($mod+(3*$i)), 3 );
			} else {
				$ret .= ',' . substr( $n, ($mod+(3*$i)), 3 );
			}
		}
		return $ret;
	}
	return $n;
}

sub check_path($$$$$$$$$);
sub check_path($$$$$$$$$) {
    my ($path,$inf,$rfndcnt,$rmin0,$rmin1,$rmin2,$rmin3,$rpathcnt,$rpotcnt) = @_;
    my ($sb,$len,$dtt,$sz,$ff,$file);
    my $tfind = $path."\\".$inf;
    my @dirs = ();
    if ( ( -f $tfind) && ($sb = stat($tfind))) {
        $dtt = YYYYMMDD($sb->mtime);
        $sz = get_nn( $sb->size );
        prt( "Found in [$path]\\$inf, $dtt, $sz bytes\n" ) if (VERB2());
        #              0     1   2    3      4
        push(@pathfnd,[$path,$sz,$dtt,$tfind,${$rpathcnt}]);
        ${$rfndcnt}++;
        $len = length($tfind);
        ${$rmin0} = $len if ($len > ${$rmin0});
        $len = length($path);
        ${$rmin1} = $len if ($len > ${$rmin1});
        $len = length($dtt);
        ${$rmin2} = $len if ($len > ${$rmin2});
        $len = length($sz);
        ${$rmin3} = $len if ($len > ${$rmin3});
    } elsif ( -d $path ) {
         # no stat - check ALL the files in this directory
         if (opendir(DIR,$path)) {
            my @files = readdir(DIR);
            closedir(DIR);
            foreach $file (@files) {
                next if (($file eq '.')||($file eq '..'));
                $ff = "$path\\$file";
                if ((-f $ff) && ($sb = stat($ff))) {
                    if ($file =~ /$inf/i) {
                        $dtt = YYYYMMDD($sb->mtime);
                        $sz = get_nn( $sb->size );
                        prt( "Found potential in [$path]\\$ff, $dtt, $sz bytes\n" ) if (VERB2());
                        #                0     1   2    3   4
                        push(@potential,[$path,$sz,$dtt,$ff,${$rpathcnt}]);
                        ${$rpotcnt}++;
                        $len = length($ff);
                        ${$rmin0} = $len if ($len > ${$rmin0});
                        $len = length($path);
                        ${$rmin1} = $len if ($len > ${$rmin1});
                        $len = length($dtt);
                        ${$rmin2} = $len if ($len > ${$rmin2});
                        $len = length($sz);
                        ${$rmin3} = $len if ($len > ${$rmin3});
                    }
                } elsif (-d $ff) {
                    push(@dirs,$ff);
                }
            }
         }
    } else {
        prt("Path: [$path] NOT VALID!\n") if (VERB1());
    }
    if ($recursive) {
        foreach $path (@dirs) {
            check_path($path,$inf,$rfndcnt,$rmin0,$rmin1,$rmin2,$rmin3,$rpathcnt,$rpotcnt);
        }
    }
}

sub find_in_path($$) {
    my ($inf,$rpatharr) = @_;
    my $path = '';
    my $tfind = '';
    my $fndcnt = 0;
    my $pcnt = scalar @{$rpatharr};
    my ($sb,$dtt,$sz,$min1,$min2,$min3,$i,$i2,$len);
    my ($cnt,@files,$file,$ff,$potcnt,$min0,$pathcnt,$msg);
    prt("Checking for [$inf], in $pcnt PATHS... ");
    $cnt = scalar @user_paths;
    if ($cnt) {
        prt("plus $cnt user paths");
        prt(" recursively") if ($recursive);
    }
    prt("\n");
    $min0 = 0;
    $min1 = 0;
    $min2 = 0;
    $min3 = 0;
    $cnt = 0;
    $potcnt = 0;
	$pathcnt = 0; # count of paths with length
    foreach $path (@{$rpatharr}) {
        $cnt++;
        $path = strip_quotes($path);
        if ($path =~ /(\\|\/)$/) {
            $path = substr($path,0,length($path)-1);
        }
        if (length($path)) {
			$pathcnt++;
            prt( "$cnt of $pcnt: Checking [$path]...\n" ) if (VERB1());
            $tfind = $path."\\".$inf;
            if ( ( -f $tfind) && ($sb = stat($tfind))) {
                $dtt = YYYYMMDD($sb->mtime);
                $sz = get_nn( $sb->size );
                prt( "Found in [$path]\\$inf, $dtt, $sz bytes\n" ) if (VERB2());
                #              0     1   2    3      4
                push(@pathfnd,[$path,$sz,$dtt,$tfind,$pathcnt]);
                $fndcnt++;
                $len = length($tfind);
                $min0 = $len if ($len > $min0);
                $len = length($path);
                $min1 = $len if ($len > $min1);
                $len = length($dtt);
                $min2 = $len if ($len > $min2);
                $len = length($sz);
                $min3 = $len if ($len > $min3);
            } elsif ( -d $path ) {
                 # no stat - check ALL the files in this directory
                 if (opendir(DIR,$path)) {
                    @files = readdir(DIR);
                    closedir(DIR);
                    foreach $file (@files) {
                        next if (($file eq '.')||($file eq '..'));
                        $ff = "$path\\$file";
                        if ((-f $ff) && ($sb = stat($ff))) {
                            if ($file =~ /$inf/i) {
                                $dtt = YYYYMMDD($sb->mtime);
                                $sz = get_nn( $sb->size );
                                prt( "Found potential in [$path]\\$ff, $dtt, $sz bytes\n" ) if (VERB2());
                                #                0     1   2    3   4
                                push(@potential,[$path,$sz,$dtt,$ff,$pathcnt]);
                                $potcnt++;
                                $len = length($ff);
                                $min0 = $len if ($len > $min0);
                                $len = length($path);
                                $min1 = $len if ($len > $min1);
                                $len = length($dtt);
                                $min2 = $len if ($len > $min2);
                                $len = length($sz);
                                $min3 = $len if ($len > $min3);
                            }
                        }
                    }
                 }
            } else {
                prt("Path: [$path] NOT VALID!\n") if (VERB1());
            }
        }
    }

    foreach $path (@user_paths) {
        $cnt++;
        $path = strip_quotes($path);
        if ($path =~ /(\\|\/)$/) {
            $path = substr($path,0,length($path)-1);
        }
        if (length($path)) {
			$pathcnt++;
            prt( "$cnt of $pcnt: Checking [$path]...\n" ) if (VERB1());
            #my ($path,$inf,$rfndcnt,$rmin0,$rmin1,$rmin2,$rmin3,$rpathcnt,$rpotcnt) = @_;
            check_path($path,$inf,\$fndcnt,\$min0,\$min1,\$min2,\$min3,\$pathcnt,\$potcnt);
        }
    }

    $min3 = $min_size if ($min3 < $min_size);

    if (($fndcnt == 0)&&($potcnt == 0)) {
        prt( "[$inf] NOT FOUND..., in any form...\n" );
    } else {
        if ($potcnt) {
            prt("Got $potcnt potential finds of [*$inf*]...\n");
            for ($i = 0; $i < $potcnt; $i++) {
                $i2 = $i + 1;
                $path = $potential[$i][0];
                $sz   = $potential[$i][1];
                $dtt  = $potential[$i][2];
                $ff   = $potential[$i][3];
                $ff .= ' ' while (length($ff) < $min0);
                $path .= ' ' while (length($path) < $min1);
                $dtt .= ' ' while (length($dtt) < $min2);
                $sz = ' '.$sz while (length($sz) < $min3);
                #prt("$i2: $path $dtt $sz\n");
                prt("$i2: $ff $dtt $sz\n");
            }
        }
        if ($fndcnt) {
            $msg = "Got $fndcnt exact finds of [$inf]...";
			$msg .= " path $pathcnt" if ($fndcnt == 1);
            prt("$msg\n");
            for ($i = 0; $i < $fndcnt; $i++) {
                $i2 = $i + 1;
                $path = $pathfnd[$i][0];
                $sz   = $pathfnd[$i][1];
                $dtt  = $pathfnd[$i][2];
                $ff   = $pathfnd[$i][3];
                $ff .= ' ' while (length($ff) < $min0);
                $path .= ' ' while (length($path) < $min1);
                $dtt .= ' ' while (length($dtt) < $min2);
                $sz = ' '.$sz while (length($sz) < $min3);
                #prt("$i2: $path $dtt $sz\n");
                prt("$i2: $ff $dtt $sz\n");
            }
        } else {
            prt("Got NO exact finds of [$inf]...\n");
        }
    }
}

#############################
# ### MAIN ###
parse_args(@ARGV);

#prt( "Checking for [$inp_file], in $pcnt PATHs ...\n" );
find_in_path($inp_file,\@patharr);

#close_log($outfile,0);
exit(0);

###########################
sub give_help() {
	prt( "Usage: $pgmname [options] filename\n" );
    prt("Options:\n");
    prt(" -h (-?)    = This help, and exit.\n");
    prt(" -p path    = Add an additional path to be searched.\n");
    prt(" -r         = Search additional path recursively.\n");
    prt(" -v[=<num]] = Set verbosity.\n");
	mydie("Will attempt to FIND the file name in the path ...\n" );
}

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av);
}

sub parse_args {
	my (@av) = @_;
	my $inf = '';
    my ($sarg);
	while (@av) {
		my $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
				give_help();
			} elsif ($sarg =~ /^v/i) {
                if ($sarg =~ /^v(\d+)$/i) {
                    $verbosity = $1;
                } elsif ($sarg =~ /^v=(\d+)$/i) {
                    $verbosity = $1;
                } elsif ($sarg =~ /^v$/i) {
                    $verbosity++;
                } else {
    				prt( "ERROR: Unknown -v argument [$arg]!\n" );
                    give_help();
                }
                prt("Bump verbosity to $verbosity...\n") if (VERB1());
            } elsif ($sarg =~ /^p/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if (-d $sarg) {
                    push(@user_paths,$sarg);
                    prt("Added user path to search $sarg...\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: Unable to locate path [$sarg].\n");
                }
            } elsif ($sarg =~ /^r/) {
                $recursive = 1;
                prt("Set recursive on user paths...\n") if (VERB1());
			} else {
				prt( "ERROR: Unknown argument [$arg]!\n" );
				give_help();
			}
		} else {
			if (length($inf)) {
				prt( "ERROR: Only find one at a time ... already have $inf, now $arg ...\n" );
				give_help();
			} else {
				$inf = $arg;
                prt("Set input file to [$inf]...\n");
			}
		}
        shift @av;
	}
	if (length($inf)) {
		$inp_file = $inf;
	} elsif ( !$debug ) {
		prt( "ERROR: No input file found ...\n" );
		give_help();
	} else {
        $inp_file = $def_inf;
        #$verbose = 9;
    }
}

# eof
