#!/usr/bin/perl -w
# NAME: bldziptbl.pl
# AIM: Pointed to a FOLDER, search all ZIP files, and tar.gz, and
# build a HTML table with headings - Also see zipindex04.pl...
# Date Link Size MD5
# 11/12/2010 - Add single file input as well...
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Cwd;
use Digest::MD5  qw(md5 md5_hex md5_base64);
use File::stat; # to get the file date and size
use File::Spec; # File::Spec->rel2abs($rel); # we are IN the SLN directory, get ABSOLUTE from RELATIVE
use Time::HiRes qw( gettimeofday tv_interval );
my $perl_dir = 'C:\GTools\perl';
unshift(@INC, $perl_dir);
require 'logfile.pl' or die "Unable to load logfile.pl ...\n";
# log file stuff
my ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $perl_dir."\\temp.$pgmname.txt";
open_log($outfile);
my $t0 = [gettimeofday];

# user variables
my $load_log = 0;
my $in_file = '';
my $out_file = $perl_dir."\\tempziptbl.htm";
my $debug_on = 1;
my $def_file = 'C:\HOMEPAGE\GA\fg\zips';
my $image_path = 'images';
my $clds_path  = 'images';
my $clds_image = "$clds_path/clds3.jpg";
my $max_count = 10;

### program variables
my @warnings = ();
my $cwd = cwd();
my $os = $^O;
my $verbosity = 0;
sub VERB1() { return ($verbosity > 0); }
sub VERB2() { return ($verbosity > 1); }
sub VERB5() { return ($verbosity > 4); }
sub VERB9() { return ($verbosity > 8); }

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 ($load_log);
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    my $elapsed = tv_interval ( $t0, [gettimeofday]);
    prt( "Ran for $elapsed seconds ...\n" ) if (VERB5());
    close_log($outfile,$load_log);
    exit($val);
}


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

sub my_file_type($) {
    my ($fil) = @_;
    return 1 if ($fil =~ /\.zip$/i);
    return 2 if ($fil =~ /\.gz$/i);
    return 3 if ($fil =~ /\.tgz$/i);
    return 0;
}

# 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;
}

#string dirghtml::b2ks1(double d) // b2ks1(double d)
sub bytes2ks($) {
	my ($d) = @_;
	my $oss;
	my $kss;
	my $lg = 0;
	my $ks = ($d / 1024); #// get Ks
	my $div = 1;
   if( $ks < 1024 ) {
      $div = 1;
      $oss = "KB";
   } elsif ( $ks < (1024 * 1024) ) {
	  $div = 1024;
      $oss = "MB";
   } elsif ( $ks < (1024 * 1024 * 1024) ) {
      $div = (1024 * 1024);
      $oss = "GB";
   } else {
      $div = (1024 * 1024 * 1024);
      $oss = "TB";
   }
   $kss = $ks / $div;
   $kss += 0.05;
   $kss *= 10;
   $lg = int($kss);
   return( ($lg / 10) . $oss );
}

sub mycmp_decend_n8 {
   return 1 if (${$a}[8] < ${$b}[8]);
   return -1 if (${$a}[8] > ${$b}[8]);
   return 0;
}
my $total_bytes = 0;
my $md5_bytes = 0;
sub process_in_dir($) {
    my ($inf) = @_;
    my @zipfiles = ();
    if (! opendir( INF, "$inf") ) {
        pgm_exit(1,"ERROR: Unable to open directory [$inf]\n"); 
    }
    my @files = readdir(INF);
    closedir INF;
    my $lncnt = scalar @files;
    prt("Processing $lncnt items, from [$inf]...\n");
    my ($file,$inc,$lnn,$dir,$ff,@dirs,$zcnt,$min,$len);
    my ($sb,$fil,$done,$md5,$dtt,$nn,$ra,$sf,$cnt,$i,$dlen,$sz);
    $dir = path_u2d($inf);
    $dir .= "\\" if ( !($dir =~ /(\\|\/)$/) );
    $ra = \@zipfiles;
    $lnn = 0;
    @dirs = ();
    $zcnt = 0;
    $dlen = length($dir);
    $min = 0;
    $md5 = "Not done yet!";
    foreach $file (@files) {
        next if (($file eq '.')||($file eq '..'));
        $ff = $dir.$file;
        if (-d $ff) {
            push(@dirs,$ff);
        } elsif (-f $ff) {
            if (my_file_type($file)) {
                if ($sb = stat($ff)) {
                    $sf = substr($ff,$dlen);
                    $len = length($sf);
                    $min = $len if ($len > $min);
                    $dtt = YYYYMMDD($sb->mtime);
                    $nn = get_nn($sb->size);
                    #${$ra}[$i][2] = -1;
                    #${$ra}[$i][3] = $md5;
                    #${$ra}[$i][4] = $dtt;
                    #${$ra}[$i][5] = $sf;
                    #${$ra}[$i][6] = $nn;
                    #${$ra}[$i][7] = $sb->size;
                    #${$ra}[$i][8] = $sb->mtime;
                    #               0   1   2  3    4    5   6   7         8 (sort)
                    push(@zipfiles,[$ff,$sf,-1,$md5,$dtt,$sf,$nn,$sb->size,$sb->mtime]);
                    $zcnt++;
                    $total_bytes += $sb->size;
                } else {
                    prtw("WARNING: stat failed on [$fil]\n");
                }
            }
        }
    }
    if (@zipfiles) {
        @zipfiles = sort mycmp_decend_n8 @zipfiles;
        prt("Got $zcnt zip files... from [$inf]...\n");
        $done = 0;
        $cnt = scalar @{$ra};
        $dlen = length($dir);
        for ($i = 0; $i < $cnt; $i++) {
            $fil = ${$ra}[$i][0];
            ### if ($sb = stat($fil)) {
                $sf = substr($fil,$dlen);
                open(FILE, $fil) or mydie( "Can't open '$fil': $!" );
				binmode(FILE);
				$md5 = Digest::MD5->new->addfile(*FILE)->hexdigest;
				close(FILE);
                $done++;
				#$dtt = YYYYMMDD($sb->mtime);
                #$nn = get_nn($sb->size);
                ${$ra}[$i][2] = 1;
                ${$ra}[$i][3] = $md5;
                $dtt = ${$ra}[$i][4];
                #${$ra}[$i][5] = $sf;
                $nn = ${$ra}[$i][6];
                $sz = ${$ra}[$i][7]; # = $sb->size;
                $md5_bytes += $sz;
                #${$ra}[$i][8] = $sb->mtime;
                $sf .= ' ' while (length($sf) < $min);
                $nn = " $nn" while (length($nn) < 14);
                prt("[$dtt] [$sf] [$nn] [$md5]\n") if (VERB5());
            ### } else {
            ###    prtw("WARNING: stat failed on [$fil]\n");
            ### }
            last if (($max_count > 0) && ($done >= $max_count));
        }
        $i = 0;
        $dtt = ${$ra}[$i][4];
        $sf  = ${$ra}[$i][5];
        $nn  = ${$ra}[$i][6];
        $md5 = ${$ra}[$i][3];
        $sf .= ' ' while (length($sf) < $min);
        $nn = " $nn" while (length($nn) < 14);
        prt("[$dtt] [$sf] [$nn] [$md5] FIRST\n");
        $i = -1;
        $dtt = ${$ra}[$i][4];
        $sf  = ${$ra}[$i][5];
        $nn  = ${$ra}[$i][6];
        $md5 = ${$ra}[$i][3];
        $sf .= ' ' while (length($sf) < $min);
        $nn = " $nn" while (length($nn) < 14);
        prt("[$dtt] [$sf] [$nn] [$md5] LAST\n");
    }
    return $ra;
}

sub get_head_html($) {
    my ($tit) = @_;
    my $head = <<EOF;
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <meta http-equiv="Content-Language"
      content="en-us">
  <meta http-equiv="Content-Type"
      content="text/html; charset=us-ascii">
  <meta name="Author"
      content="Geoff Mclane">
  <meta name="description"
      content="download zip files">
  <meta name="keywords"
      content="geoff, mclane, geoff mclane, programmer, flightgear, flight, simulator, free, source, binaries, WIN32, EXE">
  <meta name="GENERATOR"
      content="Microsoft FrontPage 5.0">
  <meta name="ProgId"
      content="FrontPage.Editor.Document">
  <title>
  $tit
  </title>
  <style type="text/css">
<!-- /* Style Definitions */
  body {
  margin: 0cm 1cm 1cm 1cm;
  background-image:url('$clds_image');
  text-align: justify;
  }
  h1 {
  background:#efefef;
  border-style: solid solid solid solid;
  border-color:#d9e2e2;
  border-width:1px;
  padding:2px 2px 2px 2px;
  font-size:200%;
  text-align:center;
  }
  tt { font-size : x-small }
  .ctr { text-align: center; }
  .cn { font-family : "Courier New"; } 
  .cnsmall { font-family : "Courier New"; font-size:70%; } 
  hr.mini { 
  margin : 0;
  border-style : none;
  padding : 0;
  width : 20%;
  text-align : center;
  }  
  p.top { 
  margin : 0;
  border-style : none;
  padding : 0;
  text-align : center;
  }
  .cn8 { font-family : "Courier New"; font-size : x-small } 
-->
  </style>
 
 </head>
EOF
    return $head;
}

sub get_table_begin() {
    my $table = <<EOF;
  <table align="center"
         border="1"
         cellpadding="1"
         cellspacing="2"
         summary="table of zips">
   <tr>
    <th>
     Date
    </th>
    <th>
     Link
    </th>
    <th>
     Size
    </th>
    <th>
     MD5
    </th>
   </tr>

EOF
    return $table;
}

sub show_zip_list($) {
    my ($ra) = @_;
    my $max = scalar @{$ra};
    my ($i,$dtt,$sf,$nn,$md5,$min,$len,$sz,$maxs,$mins,$htm,$i2,$row);
    $min = 0;
    $mins = 999999999999999999999;
    $maxs = -1;
    my $html = '';
    my $pct = 'unknown';
    if ($total_bytes > 0) {
        $pct = int((($md5_bytes / $total_bytes) * 100) + 0.5)
    }
    my $t1 = [gettimeofday];
    my $elap = tv_interval ( $t0, $t1 );
    my $bps = bytes2ks(int(($md5_bytes / $elap) + 0.5));
    #prt( "Ran for $elap seconds ...\n" ) if (VERB5());
    $row = '';
    for ($i = 0; $i < $max; $i++) {
        $i2 = $i + 1;
        $dtt = ${$ra}[$i][4];
        $sf  = ${$ra}[$i][5];
        $nn  = ${$ra}[$i][6];
        $md5 = ${$ra}[$i][3];
        $sz  = ${$ra}[$i][7];
        $len = length($sf);
        $min = $len if ($len > $min);
        $maxs = $sz if ($sz > $maxs);
        $mins = $sz if ($sz < $mins);
        $row  = "";
        $row .= "<tr>\n";
        $row .= "<td>$dtt</td>\n";
        $row .= "<td><a href=\"zips/$sf\">$sf</a></td>\n";
        $row .= "<td align=\"right\">$nn</td>\n";
        $row .= "<td nowrap><span class=\"cn8\">$md5</span></td>\n";
        $row .= "</tr>\n";
        $html .= $row;
        if (($max_count > 0) && ($i2 >= $max_count)) {
            # exit at users limit - default is 10...
            last;
        }
    }
    $html .= "<tr>\n";
    $html .= "<td colspan=\"4\">Done $i2 of total of $max zips... ".bytes2ks($md5_bytes);
    $html .= " of ".bytes2ks($total_bytes).", $pct \%, io ${bps}ps\n";
    $html .= "<br><b>Use option '-m 0' to see ALL...</b>\n" if ($i < $max);
    $html .= "</td>\n";
    $html .= "</tr>\n";

    # done inner table stuff, now add the HTML top and bottom
    # -------------------------------------------------------
    $htm = get_head_html("List of zips");
    $htm .= get_table_begin();
    $htm .= $html;
    $htm .= "</table>\n";
    $htm .= "</body>\n";
    $htm .= "</html>\n";
    write2file($htm,$out_file);
    prt("Written to [$out_file]...\n");
    system($out_file) if ($load_log);
    prt("$row\n") if ($i2 == 1);
}

sub process_in_file($) {
    my ($ff) = @_;
    my ($sf,$dir) = fileparse($ff);
    my @zipfiles = ();
    my ($sb,$dtt,$nn,$md5);
    if (-f $ff) {
        if ($sb = stat($ff)) {
            open(FILE, $ff) or mydie( "Can't open '$ff': $!" );
            binmode(FILE);
            $md5 = Digest::MD5->new->addfile(*FILE)->hexdigest;
            close(FILE);
            $dtt = YYYYMMDD($sb->mtime);
            $nn = get_nn($sb->size);
            #${$ra}[$i][2] = -1;
            #${$ra}[$i][3] = $md5;
            #${$ra}[$i][4] = $dtt;
            #${$ra}[$i][5] = $sf;
            #${$ra}[$i][6] = $nn;
            #${$ra}[$i][7] = $sb->size;
            #${$ra}[$i][8] = $sb->mtime;
            #               0   1   2  3    4    5   6   7         8 (sort)
            push(@zipfiles,[$ff,$sf,-1,$md5,$dtt,$sf,$nn,$sb->size,$sb->mtime]);
            $total_bytes += $sb->size;
        } else {
            prtw("WARNING: stat failed on [$ff]\n");
        }
    }
    return \@zipfiles; # $ra;
}

#########################################
### MAIN ###
parse_args(@ARGV);
#prt( "$pgmname: in [$cwd]: Hello, World...\n" );
my ($ref_arr);
if (-f $in_file) {
    $ref_arr = process_in_file($in_file);
} elsif (-d $in_file) {
    $ref_arr = process_in_dir($in_file);
}
show_zip_list($ref_arr);

pgm_exit(0,"");
########################################
sub give_help {
    prt("$pgmname: version 0.0.1 2010-12-07\n");
    prt("Usage: $pgmname [options] in-directory\n");
    prt("Options:\n");
    prt(" --help   (-h or -?) = This help, and exit 0.\n");
    prt(" --verbosity (-v[n]) = Bump, or set verbosity. (def=$verbosity)\n");
    prt(" --max <count>  (-m) = Set MAX output count. 0=all (def=$max_count)\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 ($arg,$sarg);
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^v/i) {
                if ($sarg =~ /^v(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/i) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Set verbosity to $verbosity\n") if (VERB1());
            } elsif ($sarg =~ /^m/i) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if ($sarg =~ /^\d+$/) {
                    $max_count = $sarg;
                    prt("Set maximum output to $max_count\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: Invalid [$arg $sarg]! 2nd has to be numeric\n");
                }
            } elsif ($sarg =~ /^l/i) {
                $load_log = 1;
                prt("Set to load log at end.\n") if (VERB1());
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = File::Spec->rel2abs($arg);
            prt("Set input to [$in_file]\n") if (VERB1());
        }
        shift @av;
    }

    if ((length($in_file) ==  0) && $debug_on) {
        $in_file = $def_file;
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -d $in_file) {
        if (! -f $in_file) {
            pgm_exit(1,"ERROR: Unable to find file or directory [$in_file]! Check name, location...\n");
        }
    }
}

# eof - bldziptbl.pl
