#!/Perl
# AIM: Scan a directory, and suggest CD-ROM write
# limits ... fill CD-ROM to below ORANGE(warn) level
# by skipping FOLDER that put it OVER this limit.
# Present limit = 700MB CD-ROM assumed, so
# LIMIT set to 650MB ...
# sort per FOLDER size
# add oldest/youngest columns
# adjust to sector sizing
# Three examples
# CD1 - 547 files, 8 dirs, 24 folders, total 647,451,959 bytes - 618MB, 316,625 sectors
# CD2 - 588 files, 9 dirs, 27 folders, total 617,984,758 bytes - 590MB, 302,262 sectors
# CD3 - 416 files, 5 dirs, 15 folders, total 446,700,056 bytes - 427MB, 218,623 sectors

use strict;
use Cwd;
use File::stat;
require "logfile.pl" or die "Missing logfile.pl ...\n"; # my simple log file and some other utility subs
my $start_time = time();

my $def_file = "C:\\Users\\Public\\SAVES\\peru\\My Pictures\\Carla";	# DEFAULT, if NO command input
###my $def_file = "C:\\HOMEPAGE";	# DEFAULT, if NO command input
my $block = 2048;	# was 512;

my @mths = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my $program = 'dirg3.pl';
my $max_cd = 650 * 1024 * 1024; # set at 650MB
my $out_name = 'tempdgpl.htm';
my $load_txt = 1;
my $load_htm = 1;
my $use_est = 1;	# use calculated total

my $cwdir = getcwd();
my $tot_all = 0;
my $tot_sub = 0;
my $no_max = 0; # set failed on FIRST - no more checking
my $had_sub = 0; # count of SUB-TOTALS issued
my $shwtm = 1;
my @in_files = (); # list of input folders

# degbug items
my $verbose = 0;
my $verb2 = 0;
my $verb3 = 0;
my $dbg = 0;
my $dbg1 = 0;	# show "Set last line = TOTALS ...
my $dbg2 = 0;	# show "Creating $out_name, with table of $row_count rows ...
my $dbg3 = 0;	# show "Done table 1 - found order ...
my $dbg4 = 0;	# show "Processing folder [$dir] ...
my $dbg5 = 1;	# show "Sub-Total: ($tot_sub + $sub_tot) > $max_cd - inserting sub-total ...
my $dbg6 = 0;	# show "Processing sub-folder [$dir] ... level $level
my $dbg7 = 0;	# show "Returning $tsz (".b2ks2($tsz).") for ".subactdir($dir).", but est is $estsiz ".b2ks2($estsiz)."...

my $fullname = 0;
my $actdir = '';
my $in_file = '';
my $tot = 0;
my $tot_dirs = 0;
my $tot_files = 0;
my $g_tot_dirs = 0;
my $g_tot_files = 0;
my $msg;
my $hdrs = "";
my $tab_width = 600;
my $row_count;
my $cdnum = 1;
my ($g_lowtm,$g_hitm);
# overall times
my $gt_lowtm = time();
my $gt_hitm = 0;
my $dbgtml = 0;
my $dbgtmh = 0;
my $mintime = 10000;	# avoid files set EARLIER than THIS!!!
my $mt = 0;
my $mincnt = 0;	# count of those BELOW this value
my @rows  = (); # hold the FINAL table ROWS x COLS
my @rows2 = (); # inserted in SIZE order, into 'columns'
my @excl_dir = ("Temporary Internet Files"); # should these be EXCLUDED???
my @undefd = (); # seems we need to 'skip' some, quietly
my @colors = qw(red blue green yellow orange purple violet);
for my $name (@colors) {
	no strict 'refs';       # allow symbol table manipulation
    *$name = *{uc $name} = sub { "<font color='$name'>@_</font>" };
}
# log file stuff
my ($LF);
my $outfile = 'temp'.$0.'.txt';
open_log($outfile);
if (@ARGV) {
	parse_arguments(@ARGV);
} else {
	#$verbose = 1;
	#$verb2 = 1;
	push(@in_files, $def_file);
}

prt( "$program: Started on " . localtime($start_time) . " in $cwdir ...\n" ) if $shwtm;

mydie( "$program: no input files found or specified\n" ) if ! @in_files;
# show count in the array ...
#prt ("Processing " . $#in_files + 1 . " directories ...\n") if $verbose;
prt ("Processing " . scalar @in_files . " directories ...\n") if $verbose;
prt( "Init Low-Time ".(scalar localtime $gt_lowtm)."($gt_lowtm)\n" ) if ($dbgtml);
prt( "Init Hi-Time  ".(scalar localtime $gt_hitm)."($gt_hitm)\n" ) if ($dbgtmh);

foreach $in_file (@in_files) {
	$actdir = retfulldir($in_file);
	if (length($hdrs)) {
		$hdrs .= "|";
	}
	$hdrs .= $actdir;
	prt ("Processing [$in_file], as [$actdir] ... moment ...\n") if ($in_file ne $actdir);
	$tot += do_user_dir($actdir);
}

prt( "Totals: $tot bytes, in $g_tot_dirs folders, $g_tot_files files ...\n" );
#prt( "$program: Got ". red($tot) . " bytes, in $tot_dirs folders, $tot_files files ...\n");

mydie( "No table rows to write to $out_name ...\n" ) if ! @rows;

prt( "Set last line = TOTALS ...\n" ) if ($dbg1);
$msg = ("Totals|$tot|$g_tot_dirs|$g_tot_files");
$msg .= "|".date_string(scalar localtime $gt_lowtm);
$msg .= "|".date_string(scalar localtime $gt_hitm);
push(@rows,$msg);
prt("$msg\n");
$row_count = scalar @rows;

# write HTML file
prt( "Creating $out_name, with table of $row_count rows ...\n" ) if ($verbose || $dbg2);

open(DSP, ">$out_name") || mydie( "Can not create $out_name: $!\n" );

#html_head(\*DSP, $hdrs);
html_head2(\*DSP, $hdrs);

print DSP "<p>Maximum CD/DVD size set to ".b2ks2($max_cd)." (was ".b2ks1($max_cd).")<br><b>Table in read/found order</b></p>\n";

print DSP "<p class='ctr'>\n";
#print DSP "<TABLE>\n";
#print DSP "<TABLE class=sbfixed border=\"1\" width=\"" . $tab_width . "\">\n";
print DSP "<table class=sbfixed border=\"1\">\n";
# column              0                                    1                                     2                              3
print DSP "<tr><td><b>Folders</b></td><td align='right'><b>Bytes</b></td><td align='right'><b>Dirs</b></td><td align='right'><b>Files</b></td>\n";
# column          4                  5
print DSP "<td><b>Low Time</b><td><b>Hi-Time</b></td></tr>\n";
my $rcnt = 0;
my $ccnt = 0;
my $bold_row = 0;
$g_lowtm = time();
$g_hitm = 0;
foreach $msg (@rows) {
	$rcnt++;
	print DSP "<tr>\n";
	$ccnt = 0;
	$bold_row = 0; # start as NOT a bold row
	my (@mcols) = split( /\|/, $msg);
	foreach my $col (@mcols) { # process EACH column
		if($rcnt == $row_count) { # ***LAST ROW*** 
			$bold_row = 1; # set BOLD ROW
		}
		if ($ccnt) {
			if ($ccnt == 1 ) { # 2nd column
				###$msg = addcolmr( b2ks1($col) );
				$msg = b2ks2($col);
			} elsif ($ccnt > 3) {
				# scalar localtime $sb->mtime
				if ($bold_row > 0) {
					if ($ccnt == 4) {
						$msg = date_string(scalar localtime $g_lowtm);
					} elsif ($ccnt == 5) {
						$msg = date_string(scalar localtime $g_hitm);
						# reset times
						$g_lowtm = time();
						$g_hitm = 0;
					}
				} else {
					$msg = date_string(scalar localtime $col);
					if ($ccnt == 4) {
						if ($col < $g_lowtm) {
						  $g_lowtm = $col;
						}
					} else {
						if ($col > $g_hitm) {
						  $g_hitm = $col;
						}
					}
				}
			} else {
				###$msg = addcolmr( get_nn($col) );
				$msg = get_nn($col);
			}
			if ($bold_row > 0) {
				$msg = addbold( $msg );
			}
			$msg = addcolmr( $msg ); # add the <td align="right"> ... </td>
		} else { # is FIRST column = TEXT
			if ($col =~ /^Sub-Total/i) {
				$bold_row = 1; # set BOLD for each COLUMN
				###$msg = addcolm( addbold( $col ) );
				$msg = $col;
			} else {
				###$msg = addcolm( $col );
				$msg = $col;
			}
			if ($bold_row > 0) {
				$msg = addbold( $msg );
			}
			###$msg = addcolm( $col );
			$msg = addcolm( $msg ); # add the <td> ... </td>
		}
		print DSP "$msg\n"; # shove it out the the HTML file
		$ccnt++;
	}
	print DSP "</tr>\n";
}
print DSP "</table>\n";
print DSP "</p>\n";
prt( "Done table 1 - found order ...\n" ) if ($dbg3);

if (scalar @undefd > 0) {
	print DSP "<p>Skipped following ...<br>\n";
	foreach $msg (@rows) {
		print DSP "$msg<br>\n";
	}
	print DSP "</p>\n";
} else {
	print DSP "<p>NONE skipped ...</p>\n";
}
if ($mincnt) {
	print DSP "<p>Note: There was/were $mincnt file(s) with a date earlier than ".(scalar localtime $mintime)."!</p>\n";
}

#my @sarr = sort( mycmp($a,$b), @rows2 );
my @sarr = sort mycmp_ascend @rows2;
my $cnt = scalar @sarr;
print DSP "<p><b>Ascending Table</b></p>\n";
out_sort_table();

@sarr = sort mycmp_decend @rows2;
$cnt = scalar @sarr;
print DSP "<p><b>Descending Table</b></p>\n";
out_sort_table();

print DSP "<p>Text results written to <a href='$outfile'>$outfile</a></p>\n";

html_tail(\*DSP);

close(DSP);

prt( "$program: Ended on " . localtime(time()) . ".\n" ) if $shwtm;
prt( "Results written to $out_name ...\n" );
close_log($outfile,$load_txt);
system( $out_name ) if ($load_htm); # start HTML file
exit(0);
##################################
# end of program

#################################
### subs
#################################
sub out_sort_table {
	prt( "Outputting sorted table ...\n" );
print DSP "<p class=\"ctr\">\n";
#print DSP "<TABLE>\n";
#print DSP "<TABLE class=sbfixed border=\"1\" width=\"" . $tab_width . "\">\n";
print DSP "<table class=\"sbfixed\" border=\"1\">\n";
print DSP "<tr>\n";
print DSP "<td><b>CD</b></td>\n";
print DSP "<td><b>Folders</b></td>\n";
print DSP "<td align=\"right\"><b>Bytes</b></td>\n";
print DSP "<td align='right'><b>Dirs</b></td>\n";
print DSP "<td align='right'><b>Files</b></td>\n";
print DSP "<td><b>Low-Time</b></td>\n";
print DSP "<td><b>Hi-Time</b></td>\n";
print DSP "</tr>\n";
my $gtbyts = 0;
my $gtdirs = 0;
my $gtfils = 0;
my $stbyts = 0;
my $stdirs = 0;
my $stfils = 0;
my $lotime = time();
my $hitime = 0;
my $tlotime = time();
my $thitime = 0;
my $lt = 0;
my $ht = 0;
my $ltt = '';
my $htt = '';
$cdnum = 1;
for (my $i = 0; $i < $cnt; $i++) {
	$lt = $sarr[$i][4];
	$ht = $sarr[$i][5];
	$ltt = scalar localtime $lt;
	$htt = scalar localtime $ht;
	if (($stbyts + $sarr[$i][0]) > $max_cd) {
		# time of a sub-total
		#$msg = ("Sub-Total|$tot_sub|$g_tot_dirs|$g_tot_files");
		if ($stbyts + $stdirs + $stfils) {
			print DSP "<tr>\n";
			print DSP "<td>".addbold($cdnum)."</td>\n";
			print DSP "<td>".addbold("Sub-Total")."</td>\n";
			print DSP "<td align='right'>".addbold(b2ks2($stbyts))."</td>\n";
			print DSP "<td align='right'>".addbold(get_nn($stdirs))."</td>\n";
			print DSP "<td align='right'>".addbold(get_nn($stfils))."</td>\n";
			print DSP "<td>".addbold(date_string(scalar localtime $lotime))."</td>\n";
			print DSP "<td>".addbold(date_string(scalar localtime $hitime))."</td>\n";
			print DPS "</tr>\n";
			$cdnum += 1;
		}
		$stbyts = 0;
		$stdirs = 0;
		$stfils = 0;
	}
	print DSP "<tr>\n";
	print DSP "<td>$cdnum</td>\n";
	print DSP "<td>".$sarr[$i][1]."</td>\n";
	print DSP "<td align='right'>".b2ks2($sarr[$i][0])."</td>\n";
	print DSP "<td align='right'>".get_nn($sarr[$i][2])."</td>\n";
	print DSP "<td align='right'>".get_nn($sarr[$i][3])."</td>\n";
	print DSP "<td>".date_string($ltt)."</td>\n";
	print DSP "<td>".date_string($htt)."</td>\n";
	print DSP "</tr>\n";
	$stbyts += $sarr[$i][0];
	$stdirs += $sarr[$i][2];
	$stfils += $sarr[$i][3];
	$gtbyts += $sarr[$i][0];
	$gtdirs += $sarr[$i][2];
	$gtfils += $sarr[$i][3];
	if ($lt < $lotime) {
		$lotime = $lt;
	}
	if ($ht > $hitime) {
		$hitime = $ht;
	}
	if ($lt < $tlotime) {
		$tlotime = $lt;
	}
	if ($ht > $thitime) {
		$thitime = $ht;
	}
	prt( $sarr[$i][1]." ".$sarr[$i][0]."\n" );
}
print DSP "<tr>\n";
print DSP "<td>".addbold($cdnum)."</td>\n";
print DSP "<td>".addbold("Sub-Total")."</td>\n";
print DSP "<td align='right'>".addbold(b2ks2($stbyts))."</td>\n";
print DSP "<td align='right'>".addbold(get_nn($stdirs))."</td>\n";
print DSP "<td align='right'>".addbold(get_nn($stfils))."</td>\n";
print DSP "<td>".addbold(date_string(scalar localtime $lotime))."</td>\n";
print DSP "<td>".addbold(date_string(scalar localtime $hitime))."</td>\n";
print DSP "</tr>\n";

print DSP "<tr>\n";
print DSP "<td>".addbold($cdnum)."</td>\n";
print DSP "<td>".addbold("Grand-Total")."</td>\n";
print DSP "<td align='right'>".addbold(b2ks2($gtbyts))."</td>\n";
print DSP "<td align='right'>".addbold(get_nn($gtdirs))."</td>\n";
print DSP "<td align='right'>".addbold(get_nn($gtfils))."</td>\n";
print DSP "<td>".addbold(date_string(scalar localtime $tlotime))."</td>\n";
print DSP "<td>".addbold(date_string(scalar localtime $thitime))."</td>\n";
print DSP "</tr>\n";
print DSP "</table></p>\n";
}

# put least first
sub mycmp_ascend {
	if (${$a}[0] < ${$b}[0]) {
		prt( "-[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
		return -1;
	}
	if (${$a}[0] > ${$b}[0]) {
		prt( "+[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
		return 1;
	}
	prt( "=[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
	return 0;
}

sub mycmp_decend {
	if (${$a}[0] < ${$b}[0]) {
		prt( "+[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
		return 1;
	}
	if (${$a}[0] > ${$b}[0]) {
		prt( "-[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
		return -1;
	}
	prt( "=[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
	return 0;
}

sub in_excluded {
	my ($d) = shift;
	foreach my $xd (@excl_dir) {
		if (uc($xd) eq uc($d)) {
			return 1;
		}
	}
	return 0;
}

sub do_user_dir { # ONLY called for ROOT scan of USER FOLDER
	my $dir = shift; # get the passed FOLDER
	prt( "Processing folder [$dir] ...\n" ) if ($verbose || $dbg4);
	if (in_excluded($dir)) {
		prt( "Directory [$dir] is in EXCLUDED list ...\n" );
		return;
	}
	my $lowtm = time();
	my $hitm = 0;
	opendir(THEDIR, $dir) || die("Couldn't open [$dir] directory\n");
	my @files = readdir(THEDIR); # slurp in ALL directories, and file, (and . & ..!)
	closedir(THEDIR);
	my $tsz = 0; # start a TOTAL for this FOLDER
	my $estsiz = 0;	# start a SECTOR TOTAL for this folder
	prt( "Found " . scalar(@files) . " files and folders ...\n" ) if $verbose;
	my @dir_list;
	foreach my $dfile (@files) {
		my $df = $dir . '/' . $dfile; # get full name
		my $sb = stat($df);
		if ( -d $df ) { # is directory?
			# if ($dfile eq '.' || $dfile eq '..') or
			if ($dfile =~ '^\.$' || $dfile =~ '^\.\.$') {
				# do nothing with DOT and DOUBLE DOT
			} else {
				if (in_excluded($dfile)) {
					prt( "Directory [$dfile] is in EXCLUDED list!\n" );
					next;
				}
			  push(@dir_list, $df); # save local DIRECTORY LIST
			  prt( "$dfile <DIR> [$df]\n" ) if $verb2;
			  if ($dbg) {
	              printf "Folder is %s, size is %s, perm %04o, mtime %s\n",
					$dfile, $sb->size, $sb->mode & 07777,
					scalar localtime $sb->mtime;
			  }
			  if ($sb->mtime < $lowtm) {
				  $lowtm = $sb->mtime;
			  }
			  if ($sb->mtime > $hitm) {
				  $hitm = $sb->mtime;
			  }
			  if ($sb->mtime < $gt_lowtm) {
				  $gt_lowtm = $sb->mtime;
				  prt( "Set Low-Time to ".(scalar localtime $gt_lowtm)." ($gt_lowtm)1\n" ) if ($dbgtml);
			  }
			  if ($sb->mtime > $gt_hitm) {
				  $gt_hitm = $sb->mtime;
				  prt( "Set Hi-Time to ".(scalar localtime $gt_hitm)." ($gt_hitm)1\n" ) if ($dbgtmh);
			  }
			  $tot_dirs++; # tsz += $block;
			  $estsiz += 3 * $block;	# estimate THREE(3) blcoks for a FOLDER
			}
		} else { # it is a FILE
			# PROCESSING A FILE
			# #################
			if (defined $sb) {
			  prt( "$dfile full [$df] ".$sb->size." bytes\n" ) if $verb2;
			  if ($dbg) {
				printf "File is %s, size is %s, perm %04o, mtime %s\n",
					$dfile, $sb->size, $sb->mode & 07777,
					scalar localtime $sb->mtime;
			  }
			  if ($sb->mtime < $lowtm) {
				  $lowtm = $sb->mtime;
			  }
			  if ($sb->mtime > $hitm) {
				  $hitm = $sb->mtime;
			  }
			  if ($sb->mtime < $gt_lowtm) {
				  $gt_lowtm = $sb->mtime;
				  prt( "Set Low-Time to ".(scalar localtime $gt_lowtm)." ($gt_lowtm)2\n" ) if ($dbgtml);
			  }
			  if ($sb->mtime > $gt_hitm) {
				  $gt_hitm = $sb->mtime;
				  prt( "Set Hi-Time to ".(scalar localtime $gt_hitm)." ($gt_hitm)2\n" ) if ($dbgtmh);
			  }
			  $tot_files++;
			  $tsz += $sb->size;
			  my $blks = int($sb->size / $block);
			  $blks++ if ($sb->size % $block);
			  $estsiz += $blks * $block;
			} else {
				push(@undefd,$df);
			}
		}
	}

	if ($fullname) {
		$msg = ("$dir is $tsz bytes ... done " . scalar @files .
			" files ($tot_files) and folders ($tot_dirs)..." );
	} else {
		$msg = ( subactdir($dir) . " is $tsz bytes ... done " . scalar @files .
			" files ($tot_files) and folders ($tot_dirs)..." );
	}
	prt( "$msg\n" ) if ($verbose);
	$g_tot_files = $tot_files;
	$g_tot_dirs = $tot_dirs;
	$tsz = $estsiz if ($use_est);
	$msg = "$actdir|$tsz|$g_tot_dirs|$g_tot_files|$lowtm|$hitm";
	push(@rows,$msg); # build up the ROW of informatiom, for the ROOT FOLDER only
	prt( "$msg\n" );	# THIS IS THE FIRST ENTRY
	push(@rows2, [$tsz,$actdir,$g_tot_dirs,$g_tot_files,$lowtm,$hitm]);
	$tot_all += $tsz; # add to ALL total
	$tot_sub += $tsz; # add to sub-total
	if ($tot_sub > $max_cd) {
		prt("Root folder has $tot_sub, which is greater than $max_cd ... NO MAX. CHECKING!\n");
		$no_max = 1; # set failed on FIRST - no more checking
	}

	# have DONE root, now process each folder
	#########################################
	foreach $dir (@dir_list) {
		$tot_files = 0;
		$tot_dirs = 0;
		my $sub_tot = do_sub_dir($dir,1);
		$tot_all += $sub_tot; # add to ALL total
		##########################################################
		### CHECK FIRST - $tot_sub += $sub_tot; # add to sub-total
		if ( ! $no_max ) { # $no_max = 0 - set if failed on FIRST - no more checking
			if (($tot_sub + $sub_tot) > $max_cd) {
				my $last_row = pop @rows;	# remove LAST element
				prt( "Sub-Total: (".b2ks2($tot_sub)." + ".b2ks2($sub_tot).") = ".b2ks2($tot_sub+$sub_tot)." > ".b2ks2($max_cd)." - inserting sub-total BEFORE last ...\n" ) if ($dbg5);
				$msg = ("Sub-Total|$tot_sub|$g_tot_dirs|$g_tot_files|*|*");
				push(@rows,$msg);
				push(@rows,$last_row) if (defined $last_row);
				$tot_sub = 0; # restart total
				$had_sub++;
			}
        }
		$tot_sub += $sub_tot; # add to sub-total
		$tsz += $sub_tot; # add to cumulative
		###$tsz += do_sub_dir($dir,1);
		$g_tot_files += $tot_files;
		$g_tot_dirs += $tot_dirs;
	}
	#########################################
	if ( ! $no_max ) {
		if (($tot_sub > $max_cd) || ($had_sub > 0)) {
			### $no_max = 1; # set failed on FIRST - no more checking
			$msg = ("Sub-Total|$tot_sub|$g_tot_dirs|$g_tot_files|*|*");
			push(@rows,$msg);
			$tot_sub = 0; # restart total
			$had_sub++;
		}
	}

	return $tsz;
}

sub do_sub_dir {
    my ($dir,$level) = @_;
	prt ("Processing sub-folder [$dir] ... level $level\n") if ($verb2 || $dbg6);
	if ($level == 1) {
		$g_lowtm = time();
		$g_hitm = 0;
	}
	opendir(THEDIR, $dir) || mydie("Couldn't open [$dir] directory\n");
	my @files = readdir(THEDIR);
	closedir(THEDIR);
	my $tsz = 0;
	my $estsiz = 0;	# estimated BLOCKS size (sectors)
	my $hdr = "";
	for (my $i = 0; $i < $level ; $i++ ) {
		$hdr .= "    ";
	}
	prt ($hdr . "Found " . scalar(@files) . " files and folders ... (l=$level)\n") if $verb2;
	foreach my $dfile (@files) {
		my $df = $dir . '/' . $dfile; # get full name
		my $sb = stat($df);
		if ( -d $df ) {
			# if ($dfile eq '.' || $dfile eq '..') or
			if ($dfile =~ '^\.$' || $dfile =~ '^\.\.$') {
				# do nothing with DOT and DOUBLE DOT
			} else {
			  if (in_excluded($dfile)) {
				  prt( "Directory [$dfile] is in EXCLUDED list!\n" );
				  next;
			  }
			  prt ($hdr . "$dfile <DIR> [$df]\n") if $verb2;
			  if ($dbg) {
	              printf "Folder is %s, size is %s, perm %04o, mtime %s\n",
					$dfile, $sb->size, $sb->mode & 07777,
					scalar localtime $sb->mtime;
			  }
			  if ($sb->mtime < $g_lowtm) {
				  $g_lowtm = $sb->mtime;
			  }
			  if ($sb->mtime > $g_hitm) {
				  $g_hitm = $sb->mtime;
			  }
			  if ($sb->mtime < $gt_lowtm) {
				  $gt_lowtm = $sb->mtime;
				  prt( "Set Low-Time to ".(scalar localtime $gt_lowtm)." ($gt_lowtm)3\n" ) if ($dbgtml);
			  }
			  if ($sb->mtime > $gt_hitm) {
				  $gt_hitm = $sb->mtime;
				  prt( "Set Hi-Time to ".(scalar localtime $gt_hitm)." ($gt_hitm)3\n" ) if ($dbgtmh);
			  }
			  prt( "Processing sub-folder $dfile ...\n" );
			  $estsiz += (3 * $block);
			  my $subtot = do_sub_dir($df,($level+1));
			  $tsz += $subtot;
			  $estsiz += $subtot;
			}
			$tot_dirs++; # count folders, and recurse into, except '.' & '..' ;=))
		} else {
			# PROCESSING A FILE
		  prt ($hdr . "$dfile full [$df] ".$sb->size." bytes\n" ) if $verb2;
		  if (defined $sb) {
			  $mt = $sb->mtime;
			  if ($dbg) {
				printf "File is %s, size is %s, perm %04o, mtime %s\n",
					$dfile, $sb->size, $sb->mode & 07777,
					scalar localtime $sb->mtime;
			  }
			  if ($mt < $mintime) {
				  $mincnt += 1;
				  prt( "WARNING: Avoiding setting Lo-Time from this file!\n" );
				  prt( "File is [$df]\nSize ".($sb->size)." perm ".($sb->mode & 07777)." mtime ".(scalar localtime $sb->mtime)." !!!\n");
			  } else {
				  if ($mt < $g_lowtm) {
					  $g_lowtm = $mt;
				  }
				  if ($mt > $g_hitm) {
					  $g_hitm = $mt;
				  }
				  if ($mt < $gt_lowtm) {
					  $gt_lowtm = $mt;
					  prt( "Set Low-Time to ".(scalar localtime $gt_lowtm)." ($gt_lowtm)4\n" ) if ($dbgtml);
				  }
				  if ($mt > $gt_hitm) {
					  $gt_hitm = $mt;
					  prt( "Set Hi-Time to ".(scalar localtime $gt_hitm)." ($gt_hitm)4\n" ) if ($dbgtmh);
				  }
			  }
			  $tot_files++;
			  $tsz += $sb->size;
			  my $blks = int($sb->size / $block);
			  $blks++ if ($sb->size % $block);
			  $estsiz += ($blks * $block);
		  } else {
			  push (@undefd, $df);
		  }
		}
	}
	$tsz = $estsiz if ($use_est);
	if ($level == 1) {
		# iteration COMPLETE - we are exiting to the ROOT
		#################################################
		if ($fullname) {
			$msg = ("$dir is $tsz bytes ... done " . scalar @files .
				" files ($tot_files) and folders ($tot_dirs)..." );
		} else {
			$msg = (subactdir($dir) . " is $tsz bytes ... done " . scalar @files .
				" files ($tot_files) and folders ($tot_dirs)..." );
		}
		prt( "$msg\n" ) if $verbose;
		# prt( addbold($msg). "\n" ) if $verbose;
		my $sad = subactdir($dir);
		### $msg = (subactdir($dir) . "|${tsz}|$tot_dirs|$tot_files");
		$msg = ("$sad|$tsz|$tot_dirs|$tot_files|$g_lowtm|$g_hitm");
		push(@rows,$msg); # build up the ROW of informatiom
		push(@rows2, [$tsz,$sad,$tot_dirs,$tot_files,$g_lowtm,$g_hitm]);
		prt( "$msg\n" );
		###################################################
	}
	prt( "Returning $tsz (".b2ks2($tsz).") for ".subactdir($dir).", but est is $estsiz ".b2ks2($estsiz)."...\n" ) if ($dbg7 && !$use_est);
	return $tsz;
}


sub parse_arguments {
 my @av = @_; # take it off the passed stack
 while (@av) {
  my $arg = $av[0];
  if ($arg eq '-i') {
	  require_argument(@av);
	  shift @av;
      $arg = $av[0];
	  if ( ! -f $arg) { mydie("ERROR: Can not locate input file '$arg'!"); }
	  open (INF, "<$arg") or mydie("ERROR: Unable to OPEN file '$arg'!");
      my @la = <INF>;	# slurp whole file
	  foreach my $f (@la) {
		  chomp $f;
		  if (length($f) > 0) {
			  if ( -f $f ) {
				  prt( "Storing file argument [$f].\n" );
				  push(@in_files, $f);
			  } elsif ( -d $f ) {
				  prt( "Storing folder argument [$f].\n" );
				  push(@in_files, $f);
			  } else {
				  prt("WARNING: Can not locate '$f' item - discarding!");
			  }
		  }
	  }
	  close(INF);
  } elsif ($arg eq '-version') {
      mydie( "Version 0.0.1\n" );
  } elsif ($arg eq '-verbose' || $arg eq '-v') {
	  prt( "Setting verbose ...\n" );
	  $verbose = 1;
  } elsif ($arg eq '-debug') {
	  prt( "Setting debug output ...\n" );
	  $dbg = 1;
  } elsif ($arg eq '-v2') {
	  prt( "Setting verb2 ...\n" );
	  $verb2 = 1;
  } elsif ($arg eq '-v3') {
	  prt( "Setting verb3 ...\n" );
	  $verb3 = 1;
  } elsif ($arg eq '-max') {
	  require_argument(@av);
	  shift @av;
	  $arg = $av[0];
	  $max_cd = $arg * 1024 * 1024;
	  prt( "Setting max CD/DVD to $max_cd (".b2ks2($max_cd).") ...\n" );
  } elsif ($arg =~ /^-/) {
	  $arg = "$0: unrecognised option? [$arg]\n";
	  $arg .= " -debug   - set debug on. Presently ".($dbg ? "ON" : "OFF")."\n";
	  $arg .= " -i fn    - use 'fn' as an input file, containing folders per line.\n";
	  $arg .= " -max nnn - maximum CD/DVD size, where nnn is in MB. Default = ".b2ks2($max_cd)."\n";
	  $arg .= " -version - displays version and exits\n";
	  $arg .= " -verbose - set verbosity (or -v). Presently ".($verbose ? "ON" : "OFF")."\n";
	  $arg .= " -v2      - set verb2. Presently ".($verb2 ? "ON" : "OFF")."\n";
	  $arg .= " -v3      - set verb3. Presently ".($verb3 ? "ON" : "OFF")."\n";
	  $arg .= " input_folder[s] - Directories to process ... recursive assumed.\n";
	  $arg .= "If none given, then current work directory used.\n";
	  mydie( "$arg\n" );
  } else {
	  prt( "Storing argument [$arg].\n" );
      push(@in_files, $arg);
  }
  shift @av; # move to next argument to [0]
 }
 push(@in_files, ".") if ! @in_files; # default to current folder
}

# Ensure argument exists, or die.
sub require_argument {
    my ($arg, @arglist) = @_;
    mydie( "$0: no argument given for option '$arg'\n" ) if ! @arglist;
}


sub dirname { # passed a path, './dir1/dir2/file.name' returns './dir1/dir2/
my ($file) = @_;
my ($sub);
($sub = $file) =~ s,/+[^/]+$,,g;
$sub = '.' if $sub eq $file;
return $sub;
}

sub retfulldir {
 my ($d) = @_;
 if ($d =~ '^\.$') {
	 $d = $cwdir; # set CURRENT WORK DIRECTORY
 } elsif ( $d =~ '^\.\.$') {
	$d = dirname( $cwdir ); # back up one ...
 }
 return $d;
}

sub subactdir {
	my ($d) = @_;
	my ($nd);
	#my $s = "s,^\$actdir,,";
	#prt ("rem $actdir frm $d use $s\n");
	#($nd = $d) =~ s,^C:/GTools/perl,,; # ok
	#($nd = $d) =~ $s; # fails???
	($nd = $d) =~ s,^$actdir,,;
	if (length($nd) == 0) {
		$nd = $actdir;
	} else {
		$nd =~ s,^/,,;
	}
#	$nd = 'root' if length $nd == 0;
	return $nd;
}

sub addbold {
	return( "<b>@_</b>" );
}

sub addcolm {
	return( "  <td>@_</td>" );
}

sub addcolmr { # string tdr = "  <TD align=\"right\">"MEOS;
	return( "  <td align=\"right\">@_</td>" );
}

sub html_head {
	my ($fh, $hdr) = @_;
	print $fh <<"EOF";
<html>
<head>
<title>$hdr</title>
</head>
<body>
<h1 align="center">$hdr</h1>
EOF

}

sub html_head2 {
	my ($os, $hdr) = @_;
   print $os "<html>\n";
   print $os "<!-- title " . $hdr . " -->\n";
   print $os "<head>\n";
   print $os "<title>" . $hdr . "</title>\n";
   print $os '<style type="text/css">'."\n";
   print $os ".ctr { text-align:center; }\n";
   print $os "body.blueform\n";
   print $os "{\n";
   print $os "    BORDER-RIGHT: #4169e1 double;\n";
   print $os "    PADDING-RIGHT: 2px;\n";
   print $os "    BORDER-TOP: #4169e1 double;\n";
   print $os "    PADDING-LEFT: 2px;\n";
   print $os "    PADDING-BOTTOM: 2px;\n";
   print $os "    MARGIN: 3px;\n";
   print $os "    BORDER-LEFT: #4169e1 double;\n";
   print $os "    PADDING-TOP: 2px;\n";
   print $os "    BORDER-BOTTOM: #4169e1 double;\n";
   print $os "    BACKGROUND-COLOR: #add8e6\n";
   print $os "}\n";
   print $os ".sbfixed\n";
   print $os "{\n";
   print $os "    ALIGN: 'center'\n";
   print $os "    COLOR: #00008b;\n";
   print $os "    FONT-FAMILY: 'Courier New';\n";
   print $os "    BACKGROUND-COLOR: #afeeee\n";
   print $os "}\n";
   print $os "</style>\n";
   print $os "</head>\n";
   print $os "<body class=\"blueform\">\n";
   print $os "\n";
   print $os "<h1 align=\"center\">" . $hdr . "</h1>\n";
}


sub html_tail {
	my ($fh) = @_;
	print $fh <<"EOF";
</body>
</html>
EOF

}

sub b2ks2 {
	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 * 1204 * 1240;
      $oss = "TB";
   }
   $kss = $ks / $div;
   $kss += 0.05;
   $kss *= 10;
   $lg = int($kss);
   return( ($lg / 10) . " " . $oss );
   ###return( ($lg / 10) . $oss );
}

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

sub get_nn { # perl nice number nicenum add commas
	my ($n) = @_;
	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 english_date_from_iso_style_date{
     my $date=shift;
     $date=~/(\d{4})(\d{2})(\d{2})/;
     my $d=Date::Handler->new({date=>{year=>$1,
          month=>$2,day=>$3}});
     return $d->MonthName().' '.$d->Day().', '.$d->Year();
}

## month to number
sub mth_to_num {
	my ($mth) = shift;
	my $cnt = 0;
	###prt( "Chk [$mth] " );
	foreach my $m (@mths) {
		$cnt++;
		if ($m eq $mth) {
			###prt( "Is $m - return $cnt\n" );
			return $cnt;
		}
	}
	prt( "WARNING: Returning 0!!!\n" );
	return '??';
}

# change localtime (Sat Mar 12 03:11:55 2005) to YYYY/MM/DD
sub date_string {
	my ($tm) = shift;
	my @arr = split( / /, $tm ); # time of form 'Sat Mar 12 03:11:55 2005'
	my $ac = scalar @arr;
	my $doff = 2;
	my $yoff = 4;
	if ($ac == 5) {
		$doff = 2;
		$yoff = 4;
	} elsif ($ac == 6) {
		$doff = 3;
		$yoff = 5;
	} else {
		mydie( "ERROR: Time ($tm) did NOT split correctly!\n" );
	}
	my $mn = mth_to_num( $arr[1] );
	if ($mn < 10) {
		$mn = '0'.$mn;
	}
	my $dn = $arr[$doff];
	if ($dn < 10) {
		$dn = '0'.$dn;
	}
	my $dtt = $arr[$yoff].'/'.$mn.'/'.$dn; # translated to 2005/03/12
	return $dtt;
}


# eof - dirg3.pl
