Generated: Tue Jun 8 17:26:43 2010 from dvdsizes.pl 2010/04/07 46.4 KB.
#!/Perl # NAME: dvdburn.pl, Version: 0.0.2 # AIM: Scan a directory, and suggest DVD write # limits ... fill DVD to below ORANGE(warn) level # by skipping FOLDER that put it OVER this limit. # Present limit = 4.7GB DVD assumed, so # LIMIT set to 4.5GB ... sort per FOLDER size # add oldest/youngest columns # adjust to sector sizing # Three examples # 2010/04/17 - point out indivual files GREATER THAN ONE DVD SIZE # 2010/01/14 - Minor adjustments # Original: 22/07/2008 dirg3.pl for 700MB, but now changed to dvdsizes.pl... # 30/10/2009 geoff mclane http://geoffair.net/mperl use strict; use warnings; use Cwd; use File::stat; use File::Find; # see http://perldoc.perl.org/File/Find.html use Data::Dumper; 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 $perl_root = "C:\\GTools\\perl"; my $outfile = $perl_root."\\temp.$pgmname.txt"; open_log($outfile); # FEATURES my $add_def_if_none = 0; my $def_file = "C:\\DTEMP"; # DEFAULT, if NO command input #my $def_file = $perl_root; # DEFAULT, if NO command input my $recursive = 1; my $block = 2048; # was 512; my $max_cd = 4.5 * 1024 * 1024 * 1024; # set at 4.5GB my $load_log = 0; my $verbose = 0; # verbosity level my $dbg_on = 0; my $use_old_way = 0; my $sort_per_size = 1; my $sort_alpha = 0; my $sort_decending = 0; # largest FIRST, else smallest first my $give_sprtf_a_go = 0; # seems L, ll, even I64u, etc all FAIL in WIN32 # Used in sub get_aligned_size_stg($) { my $fl_minn = 16; my $ks_mink = 8; my $min_file_len = 64; my $msg_sub_add = " "; my $fill_each_dvd = 1; # PROGRAM VARIABLES my @warnings = (); my @in_files = (); my $out_count = 0; my $max_count = 98; my $last_time = 0; my %exclude_dirs2 = ( 'scenery' => 1 ); my %exclude_dirs3 = ( 'FG' => 1 ); my %exclude_dirs = (); my $got_exclude_dirs = 0; my $got_exclude_root = 0; # debug my $dbg_01 = 0; # FORWARD REFS sub process_dir_array_ref($$$); sub VERB1() { return ($verbose >= 1) } sub VERB2() { return ($verbose >= 2) } sub VERB3() { return ($verbose >= 3) } # ... sub VERB5() { return ($verbose >= 5) } # ... sub VERB9() { return ($verbose >= 9) } sub prtw($) { my ($msg) = @_; prt($msg); $msg =~ s/\n$//; push(@warnings,$msg); } sub show_warnings() { if (@warnings) { prt( "Got ".scalar @warnings." WARNINGS...\n" ); foreach my $msg (@warnings) { prt( "$msg\n" ); } } else { prt( "No warnings issued.\n" ) if (VERB9()); } } sub pgm_exit($$) { my ($val,$msg) = @_; show_warnings(); if (length($msg)) { $msg .= "\n" if ( !($msg =~ /\n$/) ); prt($msg); # if (($val > 0) || VERB9()); } close_log($outfile,$load_log); exit($val); } sub prtv1($) { if (VERB1()) { prt(shift); } } sub prtso($) { print shift; } sub prtsot($) { my $txt = shift; my $ct = time(); if ($ct > $last_time) { $last_time = $ct; prtso($txt); $out_count++; if ($out_count > $max_count) { $out_count = 0; prtso("\n"); } } #print shift; #$out_count++; #if ($out_count > $max_count) { # $out_count = 0; # #print "\n"; #} } sub strip_dbl_quotes($) { my ($ln) = shift; if ($ln =~ /^".*"$/) { $ln = substr($ln,1,length($ln)-2); } return $ln; } ################################################## # 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 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); $kss = $lg / 10; $kss .= '.0' if (!($kss =~ /\./)); ###return( ($lg / 10) . " " . $oss ); return "$kss$oss"; } sub b2ks2_ok($) { 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 ); } sub find_local_files_2($) { my ($dirs) = @_; my %file_list = (); my $last_dir = ''; # $File::Find::dir - is the current directory name, # $_ - is the current filename within that directory # $File::Find::name - is the complete pathname to the file. # eg # $File::Find::dir = /some/path/ # $_ = foo.ext # $File::Find::name = /some/path/foo.ext local *wanted_files = sub { my $f = $_; my $file = $File::Find::name; my $dir = $File::Find::dir; my ($sb,$type); if (-d $file) { $type = 'DIR'; if ($dir ne $last_dir) { $last_dir = $dir; prtsot("."); } } else { $type = 'FIL'; } #return if (-d $f); #return if -l; if ($sb = stat($file)) { push (@{$file_list{$dir}{$type}}, [$file, $sb->size, $sb->mtime]); } else { prtw("WARNING: failed to 'stat' [$file]\n"); } }; File::Find::find(\&wanted_files, @{$dirs}); return %file_list; } sub mycmp_decend { return -1 if (${$a}[0] < ${$b}[0]); return 1 if (${$a}[0] > ${$b}[0]); return 0; } sub mycmp_ascend { return 1 if (${$a}[0] < ${$b}[0]); return -1 if (${$a}[0] > ${$b}[0]); return 0; } # 0 1 2 # OUT push(@summary, [$key,$siz,$blk,0]); sub mycmp_ascend_2 { return -1 if (${$a}[1] < ${$b}[1]); return 1 if (${$a}[1] > ${$b}[1]); return 0; } sub mycmp_decend_2 { return 1 if (${$a}[1] < ${$b}[1]); return -1 if (${$a}[1] > ${$b}[1]); return 0; } # $ref_sum = push(@summary, [$key,$siz,$blk,0]); sub mycmp_ascend_a { return -1 if (${$a}[0] lt ${$b}[0]); return 1 if (${$a}[0] gt ${$b}[0]); return 0; } sub mycmp_decend_a { return 1 if (${$a}[0] lt ${$b}[0]); return -1 if (${$a}[0] gt ${$b}[0]); return 0; } sub output_file_list($) { # \@file my ($ra_in) = @_; my ($file,$i,$min,$len,$cnt,$num,$ra,@arr); if ($sort_per_size) { if ($sort_decending) { @arr = sort mycmp_ascend @{$ra_in}; } else { @arr = sort mycmp_decend @{$ra_in}; } $ra = \@arr; # } elsif ($sort_alpha) { } else { $ra = $ra_in; } $cnt = scalar @{$ra}; $min = 0; for ($i = 0; $i < $cnt; $i++) { $file = ${$ra}[$i][1]; $len = length($file); $min = $len if ($len > $min); } prt( "[VERB2] List of $cnt files in root...\n" ); for ($i = 0; $i < $cnt; $i++) { $file = ${$ra}[$i][1]; $file .= ' ' while (length($file) < $min); $num = get_aligned_size_stg(${$ra}[$i][0]); prt( "$file $num\n" ); } } sub process_dir($$) { my ($dir,$lev) = @_; my (@files,$file,$ff,$sb,$DIR); my %hash = (); if ( !opendir($DIR,$dir) ) { prtw("WARNING: Unable to open directory [$dir]!\n"); return \%hash; } @files = readdir($DIR); closedir($DIR); my @dirs = (); my @fils = (); if ($lev) { prtsot('.'); # if ($lev < 2); } else { prt( "Processing directory [$dir]...($lev)\n" ); # < 2; } foreach $file (@files) { next if (($file eq '.')||($file eq '..')); $ff = $dir; $ff .= "\\" if ( !($dir =~ /(\\|\/)$/) ); $ff .= $file; if ( $sb = stat($ff) ) { if (-d $ff) { if ($recursive) { push(@dirs,[$sb->size,$file,$ff,$sb->mtime,$lev,0]); } } elsif (-f $ff) { push(@fils,[$sb->size,$file,$ff,$sb->mtime,$lev,0]); } else { prtw("WARNING: WHAT IS THIS? [$ff]\n"); } } else { prtw("WARNING: Unable to 'stat' [$ff]!\n"); } } output_file_list(\@fils) if (($lev == 0) && VERB2() ); $hash{$dir}{'DIRS'} = \@dirs; $hash{$dir}{'FILS'} = \@fils; return \%hash; } sub get_stg_left_aligned($$) { my ($stg,$min) = @_; $stg = ' '.$stg while (length($stg) < $min); return $stg; } sub get_aligned_file_size($) { my ($size) = shift; #use $fl_minn = 16; and $ks_mink = 8; return get_stg_left_aligned(get_nn($size),$fl_minn); } sub get_aligned_ks_size($) { my ($size) = shift; #use $fl_minn = 16; and $ks_mink = 8; return get_stg_left_aligned(b2ks2($size), $ks_mink); } sub get_aligned_size_stg($) { my ($size) = shift; #use $fl_minn = 16; and $ks_mink = 8; my $fsz = get_aligned_file_size($size); my $ksz = get_aligned_ks_size($size); return "$fsz $ksz"; } sub get_size_stg($) { my ($size) = shift; my $msg = " ".get_nn($size)." (".b2ks2($size).")"; return $msg; } sub process_file_array_ref($$) { my ($far_in,$rootdir) = @_; my (@sarr); if ($sort_decending) { @sarr = sort mycmp_ascend @{$far_in}; } else { @sarr = sort mycmp_decend @{$far_in}; } my $far = \@sarr; my ($ff,$file,$size,$cnt,$i); my ($total_size,$total_blocks,$minfl,$len,$minsz); my ($blks,$dsize,$dvd_num,$tsize,$tmp,$msg); my ($j,$size2,$blks2,$dsize2,$file2); $cnt = scalar @{$far_in}; $total_size = 0; $total_blocks = 0; $minfl = 0; $minsz = 0; $tsize = 0; # 0 1 2 3 4 5 # push(@fils,[$sb->size,$file,$ff,$sb->mtime,$lev,0]); for ($i = 0; $i < $cnt; $i++) { $ff = ${$far}[$i][2]; $file = ${$far}[$i][1]; $size = ${$far}[$i][0]; $len = length($file); $minfl = $len if ($len > $minfl); $total_size += $size; $blks = int($size / $block); $blks++ if (($size % $block)||($size == 0)); $total_blocks += $blks; $dsize = $blks * $block; $tmp = get_nn($dsize); $len = length($tmp); $minsz = $len if ($len > $minsz); #prt( "$file $size\n" ); ${$far}[$i][5] = 0; # clear DONE $tsize += $dsize; # accumulate total, by each file in root } $blks = $total_blocks * $block; prt( "Total $cnt files, total size = $total_size ($blks) or ".b2ks2($total_size)." (".b2ks2($blks).")\n" ); $dvd_num = 1; #if ($blks > $max_cd) { if ($tsize > $max_cd) { $tsize = 0; # restart total size for THIS DVD for ($i = 0; $i < $cnt; $i++) { next if (${$far}[$i][5]); # skip it if DONE $ff = $sarr[$i][2]; $file = $sarr[$i][1]; $size = $sarr[$i][0]; ${$far}[$i][5] = 1; # DONE THIS FILE $blks = int($size / $block); $blks++ if (($size % $block)||($size == 0)); $dsize = $blks * $block; if ($dsize > $max_cd) { # YEEK, this ONE FILE *GT* DVD SIZE if ($tsize) { if ($fill_each_dvd) { # seek any smaller file to add to this DVD for ($j = 0; $j < $cnt; $j++) { next if (${$far}[$j][5]); # skip it if DONE $size2 = $sarr[$j][0]; $file2 = $sarr[$j][1]; $blks2 = int($size2 / $block); $blks2++ if (($size2 % $block)||($size2 == 0)); $dsize2 = $blks2 * $block; $tmp = get_aligned_size_stg($dsize2); prt( "[dbg_01] Check $file2 $tmp \n") if ($dbg_01); if (($tsize + $dsize2) <= $max_cd) { # found one that can be ADDED if (VERB1()) { $file2 .= ' ' while (length($file2) < $minfl); prt("$file2 $tmp - ADDED\n"); } $tsize += $dsize2; ${$far}[$j][5] = 1; # DONE THIS FILE } } } $msg = "$dvd_num: SUBTOTAL "; $msg .= " " while (length($msg) < $minfl); $msg .= $msg_sub_add; #prt( "$dvd_num: Subtotal ".get_size_stg($tsize)."\n" ); #prt( "$msg".get_size_stg($tsize)."\n" ); $tmp = get_aligned_size_stg($tsize); prt( "$msg $tmp\n" ); $dvd_num++; } if (VERB1()) { $file .= ' ' while (length($file) < $minfl); $tmp = get_aligned_size_stg($dsize); prt("$file $tmp\n"); } $msg = "$dvd_num: SUBTOTAL "; $msg .= " " while (length($msg) < $minfl); $msg .= $msg_sub_add; #prt( "$dvd_num: Subtotal ".get_size_stg($dsize)." - NEEDS FURTHER SPLIT\n" ); #prt( "$msg".get_size_stg($dsize)." - NEEDS FURTHER SPLIT\n" ); $tmp = get_aligned_size_stg($dsize); if (!VERB1()) { prt( "$msg $tmp - file [$file(".b2ks2($size).")] NEEDS FURTHER SPLIT!\n" ); } else { prt( "$msg $tmp - NEEDS FURTHER SPLIT!\n" ); } $dvd_num++; $tsize = 0; next; # go for NEXT } elsif (($tsize + $dsize) > $max_cd) { # Ok, current rnning TOTAL PLUS THIS FILE GT DVD SIZE if ($fill_each_dvd) { # seek any smaller file to add to this DVD for ($j = 0; $j < $cnt; $j++) { next if (${$far}[$j][5]); # skip it if DONE $size2 = $sarr[$j][0]; $file2 = $sarr[$j][1]; $blks2 = int($size2 / $block); $blks2++ if (($size2 % $block)||($size2 == 0)); $dsize2 = $blks2 * $block; $tmp = get_aligned_size_stg($dsize2); prt( "[dbg_01] Check $file2 $tmp \n") if ($dbg_01); if (($tsize + $dsize2) <= $max_cd) { # found one that can be ADDED if (VERB1()) { $file2 .= ' ' while (length($file2) < $minfl); prt("$file2 $tmp - ADDED\n"); } $tsize += $dsize2; ${$far}[$j][5] = 1; # DONE THIS FILE } } } $msg = "$dvd_num: SUBTOTAL "; $msg .= " " while (length($msg) < $minfl); $msg .= $msg_sub_add; #prt( "$dvd_num: Subtotal ".get_size_stg($tsize)."\n" ); #prt( "$msg".get_size_stg($tsize)."\n" ); $tmp = get_aligned_size_stg($tsize); prt( "$msg $tmp\n" ); $tsize = 0; $dvd_num++; } $tsize += $dsize; # add this size to TOTAL CURRENT DVD size if (VERB1()) { $file .= ' ' while (length($file) < $minfl); #prt("$file $dsize (".b2ks2($dsize).")\n"); #$tmp = get_nn($dsize); #$tmp = " $tmp" while (length($tmp) < $minsz); #prt("$file $tmp (".b2ks2($dsize).")\n"); $tmp = get_aligned_size_stg($dsize); prt("$file $tmp\n"); } } } else { # root files will ALL fit on ONE DVD if (VERB1()) { if ($cnt) { for ($i = 0; $i < $cnt; $i++) { # want LIST of files $ff = $sarr[$i][2]; $file = $sarr[$i][1]; $size = $sarr[$i][0]; $blks = int($size / $block); $blks++ if (($size % $block)||($size == 0)); $dsize = $blks * $block; $file .= ' ' while (length($file) < $minfl); $tmp = get_aligned_size_stg($dsize); prt("$file $tmp\n"); } } else { prt("There are NO files to list in the 'root' folder....\n"); } } } if ($tsize) { $msg = "$dvd_num: SUBTOTAL "; $msg .= " " while (length($msg) < $minfl); $msg .= $msg_sub_add; #prt( "$dvd_num: Subtotal ".get_size_stg($tsize)."\n" ); #prt( "$msg".get_size_stg($tsize)."\n" ); $tmp = get_aligned_size_stg($tsize); prt( "$msg $tmp\n" ); } if ($total_blocks) { prt("For [$rootdir], need $dvd_num DVDS, for total ".get_size_stg($total_blocks * $block)); if ($got_exclude_root) { prt(",\n but root files are EXCLUDED."); } prt("\n"); # my ($j,$size2,$blks2,$dsize2,$file2); $size2 = $total_blocks * $block; $j = int( $size2 / $max_cd ); $dsize2 = $size2 - ($j * $max_cd); $j++ if ($dsize2); prt("So this root needs $j dvds, if evenly split.. last dvd with ".b2ks2($dsize2)." bytes.\n") if ($j > $dvd_num); } else { prt("No files in root [$rootdir]...\n") if (VERB9()); } my @rtots = (); # prt( "push(\@rtots, [ $total_size, $total_blocks, $tsize, $dvd_num ]);\n" ); push(@rtots, [ $total_size, $total_blocks, $tsize, $dvd_num ]); return \@rtots; } sub process_dir_array_ref($$$) { my ($rdirs,$rhash,$lev) = @_; my $cnt = scalar @{$rdirs}; my ($i,$dir,$rdh,$rd2); #my %hash = (); for ($i = 0; $i < $cnt; $i++) { # 0 1 2 3 # push(@dirs,[$sb->size,$file,$ff,$sb->mtime]); $dir = ${$rdirs}[$i][2]; $rdh = process_dir($dir,$lev); ${$rhash}{$dir} = $rdh; #$hash{$dir} = $rdh; $rd2 = ${$rdh}{$dir}{'DIRS'}; process_dir_array_ref($rd2,$rhash,($lev + 1)); } #foreach $dir (keys %hash) { # $rdh = $hash{$dir}; # $rd2 = ${$rdh}{$dir}{'DIRS'}; # process_dir_array_ref($rd2,$rhash,($lev + 1)); #} } sub remove_base2($$) { my ($file,$key) = @_; my $len = length($file); if (($len+1) < length($key)) { # remove the base return substr($key,($len+1)); } return $key; } sub process_full_hash($$) { my ($rhash,$base) = @_; my ($dcnt,$rdh,$rfils,$fcnt); $dcnt = scalar keys(%{$rhash}); prt( "Got $dcnt keys to process...\n" ); my ($key,$skey,$nkey,$i,$ff,$fil,$siz); my ($size,$total_size); $nkey = ''; $total_size = 0; my @dir_array = (); foreach $key (sort keys(%{$rhash})) { $rdh = ${$rhash}{$key}; if ($key eq '*FILES_ARRAY*') { prt("Processing key [$key]...\n") if (VERB2()); } elsif ($key eq '*DIRECTORY_ARRAY*') { prt("Processing key [$key]...\n") if (VERB2()); } else { # $hash{$dir}{'DIRS'} = \@dirs; # $hash{$dir}{'FILS'} = \@fils; $rfils = ${$rdh}{$key}{'FILS'}; $fcnt = scalar @{$rfils}; $skey = remove_base2($base,$key); if ( !($skey =~ /(\\|\/)/) ) { # if a base subdirectory... if (length($nkey)) { prt( "Done [$nkey] total = $total_size\n" ) if (VERB2()); push(@dir_array, [ $nkey, $total_size ]); } prt( "Processing base [$skey]\n" ) if (VERB2()); $nkey = $skey; $total_size = 0; } for ($i = 0; $i < $fcnt; $i++) { $ff = ${$rfils}[$i][2]; $fil = ${$rfils}[$i][1]; $siz = ${$rfils}[$i][0]; $total_size += $siz; } } } if (length($nkey)) { prt( "Done [$nkey] total = $total_size\n" ) if (VERB2()); push(@dir_array, [ $nkey, $total_size ]); } ${$rhash}{'*DIRECTORY_ARRAY*'} = [ @dir_array ]; return \@dir_array; } #1234567890123456 #000,000,000,000 sub show_hash_reference($) { my ($hr) = @_; my $rdh = ${$hr}{'*FILES_ARRAY*'}; my $rda = ${$hr}{'*DIRECTORY_ARRAY*'}; my ($dir,$rdirs,$rfils,$dcnt,$fcnt,$i,$siz,$size); my ($min,$len); my $minn = $fl_minn; # = 16; if (defined $rdh) { foreach $dir (keys %{$rdh}) { $rdirs = ${$rdh}{$dir}{'DIRS'}; $rfils = ${$rdh}{$dir}{'FILS'}; $dcnt = scalar @{$rdirs}; $fcnt = scalar @{$rfils}; prt( "Got $fcnt files, from [$dir], and $dcnt directories...\n" ); } } if (defined $rda) { $dcnt = scalar @{$rda}; $min = 0; for ($i = 0; $i < $dcnt; $i++) { $dir = ${$rda}[$i][0]; $len = length($dir); $min = $len if ($len > $min); } for ($i = 0; $i < $dcnt; $i++) { $dir = ${$rda}[$i][0]; $size = ${$rda}[$i][1]; $siz = get_nn($size); $dir .= ' ' while (length($dir) < $min); $siz = ' '.$siz while (length($siz) < $minn); prt( "$dir | $siz (".b2ks2($size).")\n" ); } } } sub is_in_excluded($) { my ($dir) = shift; return 1 if (defined $exclude_dirs{$dir}); return 0; } sub dir_is_in_excluded($) { my ($path) = shift; my @arr = split(/(\\|\/)/, $path); foreach my $dir (@arr) { if (defined $exclude_dirs{$dir}) { return 1; } } return 0; } sub find_local_files($) { my ($dirs) = @_; my %file_list = (); # $File::Find::dir - is the current directory name, # $_ - is the current filename within that directory # $File::Find::name - is the complete pathname to the file. # eg # $File::Find::dir = /some/path/ # $_ = foo.ext # $File::Find::name = /some/path/foo.ext local *wanted_files = sub { push (@{$file_list{$File::Find::dir}}, $_); }; File::Find::find(\&wanted_files, @{$dirs}); return \%file_list; } sub process_dir_hash($$$) { my ($rdirs,$rhash,$rootdir) = @_; my $cnt = scalar @{$rdirs}; my ($i,$dir,$rdh,$rd2); #my %hash = (); my $bgn_secs = time(); my $skipped = 0; return if (!$cnt); prt( "Doing $cnt sub-directories of [$rootdir], with File::Find...\n" ); for ($i = 0; $i < $cnt; $i++) { # 0 1 2 3 # push(@dirs,[$sb->size,$file,$ff,$sb->mtime]); $dir = ${$rdirs}[$i][2]; if ( dir_is_in_excluded($dir) ) { if (VERB5()) { prt("Excluded directory [$dir]\n"); } else { prtso(".") if (VERB2()); } $skipped++; next; } else { if (VERB5()) { prt("Processing folder [$dir]\n"); } else { prtso(".") if (VERB2()); } } # find_local_files return \%file_list; ${$rhash}{$dir}{'FF'} = find_local_files( [$dir] ); } prtso("\n") if (VERB2() && !VERB5()); if ($got_exclude_dirs) { if ($skipped) { if ($skipped == $got_exclude_dirs) { prt("Skipped $skipped of $got_exclude_dirs excluded directories.\n") if (VERB2()); } else { prtw("WARNING: Skipped $skipped of $got_exclude_dirs excluded directories!\n"); } } else { prtw("WARNING: Got $got_exclude_dirs excluded directories, BUT NONE found!\n"); } } my $elap_secs = time() - $bgn_secs; prt( "Done $cnt sub-directories, in $elap_secs seconds, with File::Find...\n" ) if (VERB5()); } sub remove_base($$) { my ($file,$key) = @_; my $len = length($file); my $base = ''; if ($len < length($key)) { # remove the base $base = substr($key,$len); $base =~ s/^(\\|\/)//; return $base; } elsif ($len == length($key)) { return '.'; } return $key; } sub expand_dir_hash($$$) { my ($rdirs,$rhash,$base) = @_; # ${$rhash}{$dir}{'FF'} = find_local_files( [$dir] ); # push (@{$file_list{$File::Find::dir}}, $_); my $dircnt = scalar keys(%{$rhash}); my ($dir,$rff,$item,$fcnt,$dcnt,$ff,$fref); my ($ditem,$fcnt2,$file,$sb,$sitem); my ($tsize,$kb,$flist,$size,$blks,$tblks,$tbsize); my ($sdir,$len,$min,$cnt); my %hash = (); if (!$dircnt) { return \%hash; } prt("Expanding [$base] sub-directory hash for $dircnt keys...\n") if (VERB3()); $min = 0; my $bgtime = time(); foreach $dir (keys(%{$rhash})) { $sdir = remove_base($base,$dir); $len = length($sdir); $min = $len if ($len > $min); } foreach $dir (sort keys(%{$rhash})) { $rff = ${$rhash}{$dir}{'FF'}; $cnt = scalar keys(%{$rff}); $sdir = remove_base($base,$dir); if (VERB5()) { prt("dir: ".sprintf("%".$min."s", $sdir)." - "); } else { prtso(".") if (VERB2()); } $fcnt = 0; $dcnt = 0; $fcnt2 = 0; $tsize = 0; $flist = ''; $tblks = 0; foreach $item (keys %{$rff}) { $fref = ${$rff}{$item}; $fcnt += scalar @{$fref}; $ditem = $item; $ditem =~ s/\//\\/g; $sitem = remove_base($dir,$item); $flist .= '|' if (length($flist)); $flist .= "$sitem($fcnt)"; # array of file, and directory entries if (-d $ditem) { $dcnt++; $ditem .= "\\" if ( !($ditem =~ /\\$/) ); foreach $file (@{$fref}) { $ff = $ditem . $file; if ($sb = stat($ff)) { $size = $sb->size; $blks = int($size / $block); $blks++ if (($size % $block)||($size == 0)); #push(@{$hash{$sitem}}, [$file, $size, $sb->mtime, $blks]); $tsize += $size; $tblks += $blks; } else { prt("\n"); prtw("WARNING: NO STAT ON [$ff]!\n"); } } } elsif (-f $ditem) { $fcnt2++; } else { prt("\n"); prtw("WARNING: GOT [$item] or [$ditem] NOT FILE OR DIRECTORY!\n"); #prt( Dumper($rff) ); } } $kb = b2ks2($tsize); $tbsize = $tblks * $block; push(@{$hash{$sdir}}, [$tsize, $tblks]); prt( sprintf("%4u",$fcnt)." files, ".get_aligned_size_stg($tsize).", or ".get_aligned_file_size($tbsize)." on disk.\n" ) if (VERB2()); prt( "In $dcnt($cnt) folders... [$flist]\n" ) if (VERB3()); } prtso("\n") if (VERB2() && !VERB5()); my $difftime = time() - $bgtime; prt("Done $dircnt expansions, in $difftime seconds...\n") if (VERB9()); return \%hash; } sub no_case_ascend { my $lca = lc($a); my $lcb = lc($b); return -1 if ($lca lt $lcb); return 1 if ($lca gt $lcb); return 0; } # IN show_dir_sizes($ref_hash,$ref_rt,$file); # OUT push(@summary, [$key,$siz,$blk,0]); sub show_dir_sizes($$$) { my ($rh,$rt,$root) = @_; # push(@{$hash{$sdir}}, [$tsize, $tblks]); my @arr = sort no_case_ascend keys(%{$rh}); my ($key,$min,$len,$minn); my ($rsa,$rsaa,$siz,$blk,$csiz); my ($cblk,$kblk,$mink,$tmp); my $tot_size = 0; my $tot_blks = 0; my $dircnt = scalar @arr; $minn = $fl_minn; # 16; $mink = $ks_mink; # 8; $min = 0; my @summary = (); if (!$dircnt) { return \@summary; } prt("Getting summary of $dircnt folders, in [$root]...\n"); foreach $key (@arr) { $len = length($key); $min = $len if ($len > $min); } # Add the ROOT to the SUMMARY if ( ! $got_exclude_root ) { $key = "$root (*)"; $len = length($key); $min = $len if ($len > $min); $rsa = ${$rt}[0]; # hmmm, WHY is this double layered??? $siz = ${$rsa}[0]; $blk = ${$rsa}[1]; push(@summary, [$key,$siz,$blk,0]); $tmp = $blk * $block; # prt( "push(\@summary, [$key,$siz,$blk,0]); ($blk * $block = $tmp)\n"); $tot_size += $siz; $tot_blks += $blk; if (VERB2()) { $key .= ' ' while (length($key) < $min); $csiz = get_nn($siz); $csiz = ' '.$csiz while (length($csiz) < $minn); $cblk = get_nn($blk * $block); $cblk = ' '.$cblk while (length($cblk) < $minn); $kblk = b2ks2($blk * $block); $kblk = ' '.$kblk while (length($kblk) < $mink); prt("$key | $csiz ($cblk $kblk) ".sprintf("%8u",$blk)." blocks\n"); } } foreach $key (@arr) { $rsaa = ${$rh}{$key}; #prt( Dumper($rsa) ); #exit(1); $rsa = ${$rsaa}[0]; # hmmm, WHY is this double layered??? $siz = ${$rsa}[0]; $blk = ${$rsa}[1]; push(@summary, [$key,$siz,$blk,0]); # prt( "push(\@summary, [$key,$siz,$blk,0]);\n" ); $tot_size += $siz; $tot_blks += $blk; if (VERB2()) { $key .= ' ' while (length($key) < $min); $csiz = get_nn($siz); $csiz = ' '.$csiz while (length($csiz) < $minn); $cblk = get_nn($blk * $block); $cblk = ' '.$cblk while (length($cblk) < $minn); $kblk = b2ks2($blk * $block); $kblk = ' '.$kblk while (length($kblk) < $mink); prt("$key | $csiz ($cblk $kblk) ".sprintf("%8u",$blk)." blocks\n"); } } $key = "TOTAL: "; if (VERB2()) { $key .= ' ' while (length($key) < $min); } $csiz = get_nn($tot_size); $csiz = ' '.$csiz while (length($csiz) < $minn); $cblk = get_nn($tot_blks * $block); $cblk = ' '.$cblk while (length($cblk) < $minn); $kblk = b2ks2($tot_blks * $block); $kblk = ' '.$kblk while (length($kblk) < $mink); prt("$key | $csiz ($cblk $kblk) ".sprintf("%8u",$tot_blks)." blocks\n"); prt("Done summary of $dircnt folders, in [$root]...\n"); return \@summary; } sub add_padding($$) { my ($rtxt,$min) = @_; ${$rtxt} = ' '.${$rtxt} while (length(${$rtxt}) < $min); } # divide_for_dvd($ref_sum,$ref_rt,$file); # $ref_sum = push(@summary, [$key,$siz,$blk,0]); sub divide_for_dvd($$$) { my ($ra_in,$rroot,$rootdir) = @_; my ($ra,@arr); if ($sort_per_size) { if ($sort_decending) { @arr = sort mycmp_decend_2 @{$ra_in}; } else { @arr = sort mycmp_ascend_2 @{$ra_in}; } $ra = \@arr; } elsif ($sort_alpha) { if ($sort_decending) { @arr = sort mycmp_decend_a @{$ra_in}; } else { @arr = sort mycmp_ascend_a @{$ra_in}; } $ra = \@arr; } else { $ra = $ra_in; } my $cnt = scalar @{$ra}; if (!$cnt) { prt( "No count, so divide_for_dvd function abandoned...\n") if (VERB9()); return; } my ($i,$dir,$siz,$blk,$min,$len,$tot_blks,$cblks); my ($bpdvd,$dvd_num,$msg,$dsize); my ($form,$tmp1,$tmp2,$tmp3,$tmp4); my ($j,$blk2,$siz2,$msg2,$rem,$dsize2,$dir2,$tmp); my ($srched,$added); $min = 0; $tot_blks = 0; $cblks = 0; $dvd_num = 1; $bpdvd = int($max_cd / $block); prt( "Division to DVD blocks, on $cnt items... max $bpdvd blocks [".get_size_stg($max_cd)."]\n" ); for ($i = 0; $i < $cnt; $i++) { $dir = ${$ra}[$i][0]; $siz = ${$ra}[$i][1]; $blk = ${$ra}[$i][2]; ${$ra}[$i][3] = 0; # clear DONE flag $len = length($dir); $min = $len if ($len > $min); $tot_blks += $blk; $dsize = $blk * $block; if ($blk > $bpdvd) { if ($give_sprtf_a_go) { $form = '%'.$min.'s'." %14L %14L"; $msg = sprintf( $form, $dir, $dsize, $blk); # FAILS WITH LARGE INTEGERS!!! } else { $tmp1 = $dir; # add_padding(\$tmp1,$min); $tmp2 = get_aligned_size_stg($dsize); $tmp3 = "$blk"; add_padding(\$tmp3,10); $msg = $tmp1.$tmp2.$tmp3; } $msg = substr($msg,1) while ($msg =~ /^\s/); prt("NOTE: [$msg] NEEDS FURTHER SPLITTING\n") if (VERB3()); #prt( "blocks $blk * $block = $dsize\n"); } } $form = '%'.$min.'s'." %14Lu %14I64u"; # seems L, ll, even I64 all FAIL in WIN32 for ($i = 0; $i < $cnt; $i++) { next if (${$ra}[$i][3]); # already DONE $dir = ${$ra}[$i][0]; $siz = ${$ra}[$i][1]; $blk = ${$ra}[$i][2]; ${$ra}[$i][3] = 1; # set DONE flag $dsize = $blk * $block; if ($give_sprtf_a_go) { $msg = sprintf( $form, $dir, $dsize, $blk); } else { $tmp1 = $dir; add_padding(\$tmp1,$min); $tmp2 = get_aligned_size_stg($dsize); $tmp3 = "$blk"; add_padding(\$tmp3,10); $msg = $tmp1.$tmp2.$tmp3; } if ($blk > $bpdvd) { $tmp = $blk; # put BLOCKS of this directory into a TEMP if ($cblks) { # try to FILL this with some from the LARGE... $rem = $bpdvd - $cblks; # get REMAINDER blocks on this DVD, about to be CLOSED $tmp -= $rem; # remove this remainder from the LARGE $cblks += $rem; # and ADD to this DVD blocks prt("End DVD $dvd_num: ".get_size_stg($cblks * $block).", filled $rem blocks from [$dir] [".get_size_stg($rem * $block)."]\n"); $dvd_num++; # closed this DVD $cblks = 0; } while ($tmp > $bpdvd) { # block count still above a DVD size prt( "$dvd_num: $msg = Note, $bpdvd of $tmp blocks put on this DVD!\n" ); $dvd_num++; $tmp -= $bpdvd; # subtract ONE DVD of blocks $msg = "$dir"; # reduce message to just DIRECTORY name add_padding(\$msg,$min); # and fill out the length } prt( "$dvd_num: $msg - Note, $tmp of $blk blocks carried to next DVD! [".get_size_stg($tmp * $block)."]\n" ) if ($tmp); $cblks = $tmp; # start this new DVD, with this remainder, if ANY next; # and go get NEXT item } elsif (($cblks + $blk) > $bpdvd) { if ($cblks) { # try to FILL this with some smaller directory... $rem = $bpdvd - $cblks; $srched = -1; $added = 0; if ($rem > 10) { # if there is room for at least 10 blocks $srched = 0; for ($j = 0; $j < $cnt; $j++) { # go through the LIST again next if (${$ra}[$j][3]); # SKIP if already DONE $blk2 = ${$ra}[$j][2]; # get its block size $srched++; if ($blk2 < $rem) { # if LESS than remainder # got ONE, ADD it to THIS DVD $dir2 = ${$ra}[$j][0]; # extract DIRECTORY $siz2 = ${$ra}[$j][1]; # and size ${$ra}[$j][3] = 1; # set DONE flag $dsize2 = $blk2 * $block; $tmp1 = $dir2; add_padding(\$tmp1,$min); $tmp2 = get_aligned_size_stg($dsize2); $tmp3 = "$blk2"; add_padding(\$tmp3,10); $msg2 = $tmp1.$tmp2.$tmp3; prt( "$dvd_num: $msg2 ($j): Added to DVD [".get_size_stg($siz2)."]\n" ); $rem -= $blk2; $cblks += $blk2; $added++; } } } prt("End DVD $dvd_num: ".get_size_stg($cblks * $block)); if ($srched == -1) { prt(" No srch - due rem($rem) < 10" ); } elsif ($srched == 0) { prt(" No srch - none not Done of $cnt!" ); } else { if ($added) { prt(" Srched $srched, of $cnt, added $added, rem=$rem" ); } else { prt(" Srched $srched, of $cnt, but none less that rem=$rem" ); } } prt("\n"); $dvd_num++; $cblks = 0; } $cblks = 0; } $cblks += $blk; prt( "$dvd_num: $msg (".($i+1).")\n" ); } if ($cblks) { $tmp = int(($cblks * 10000) / $bpdvd); $tmp /= 100; prt("End DVD $dvd_num: ".get_size_stg($cblks * $block)." ($tmp".'%'.")\n"); } # summary - what it is all about # ============================== prt( "For [$rootdir] need $dvd_num DVDs for ".get_size_stg($tot_blks * $block) ); if ($got_exclude_root) { prt( ", excluding ROOT" ); } if ($got_exclude_dirs) { prt(",\n excluding $got_exclude_dirs dir(s) ["); foreach $tmp (keys %exclude_dirs) { prt("$tmp "); } prt("]"); } prt("\n"); } sub process_in_files($) { my ($rinfs) = shift; my ($file,$rdh,$rdirs,$rfils,$dcnt,$fcnt,$rfd); my %hash = (); my %hash2 = (); foreach $file (@{$rinfs}) { if (-d $file) { $rdh = process_dir($file,0); $hash{'*FILES_ARRAY*'} = $rdh; $rdirs = ${$rdh}{$file}{'DIRS'}; $rfils = ${$rdh}{$file}{'FILS'}; $dcnt = scalar @{$rdirs}; $fcnt = scalar @{$rfils}; if ($recursive) { prt( "Got $dcnt directories, and $fcnt files, from [$file]\n" ); } else { prt( "Got $fcnt files, from [$file] - recursive is OFF.\n" ); } my $ref_rt = process_file_array_ref($rfils,$file); # count the files in the ROOT space if ($use_old_way) { # this was TOO SLOW, so tried another WAY ;=)) my $bgn_secs = time(); process_dir_array_ref($rdirs,\%hash,1); my $elap_secs = time() - $bgn_secs; prt( "\nDone $dcnt directories in $elap_secs seconds...\n" ); process_full_hash(\%hash,$file); } else { process_dir_hash($rdirs,\%hash2,$file); # get all the sub-directories my $ref_hash = expand_dir_hash($rdirs,\%hash2,$file); my $ref_sum = show_dir_sizes($ref_hash,$ref_rt,$file); divide_for_dvd($ref_sum,$ref_rt,$file); } } } return \%hash; } # ============================= # MAIN parse_arguments(@ARGV); $| = 1; if (@in_files) { my $hash_ref = process_in_files(\@in_files); if ($use_old_way) { show_hash_reference($hash_ref); } } else { pgm_exit(1,"No input directory to process... Try -?"); } pgm_exit(0,"Normal exit"); # ============================= # Ensure argument exists, or die. sub require_argument { my ($arg, @arglist) = @_; if (!@arglist) { prt( "ERROR: no argument given for option '$arg', and one is required!\n" ); pgm_exit(1,"Bad parameter"); } } # -i <FILE> fo rinput sub deal_with_i_opt($) { my ($arg) = shift; if ( ! -f $arg) { prt("ERROR: Can not locate input file '$arg'!"); pgm_exit(1,"No file"); } if ( !open (INF, "<$arg") ) { prt("ERROR: Unable to OPEN file '$arg'!"); pgm_exit(1,"File Error"); } my @la = <INF>; # slurp whole file close(INF); foreach my $f (@la) { chomp $f; if (length($f) > 0) { if ( -f $f ) { prtv1( "Storing file argument [$f].\n" ); push(@in_files, $f); } elsif ( -d $f ) { prtv1( "Storing folder argument [$f].\n" ); push(@in_files, $f); } else { prtw("WARNING: Can not locate '$f' item - discarding!"); } } } } sub deal_with_v_opt($) { my ($arg) = shift; my ($tmp,$len,$i,$cc); if ($arg =~ /^-v(\d+)$/) { # -v<num> $tmp = $1; $verbose = $tmp; } elsif ($arg =~ /^-v/) { $verbose++; if ($arg =~ /^-v/) { $tmp = substr($arg,2); $len = length($tmp); # process for a set of vvvvv's - squeak if one is NOT a 'v' for ($i = 0; $i < $len; $i++) { $cc = substr($tmp,$i,1); if ($cc eq 'v') { $verbose++; } else { prt( "ERROR: unrecognised option? [$arg]! Try -? ... aborting...\n" ); pgm_exit(1,"Bad -v Option"); } } } prtv1( "Set verbose to $verbose\n" ); } } # the SORT option sub deal_with_s_opt($) { my ($arg) = shift; if ($arg eq '-soff') { $sort_per_size = 0; prtv1( "Set size sort OFF.\n" ) if (VERB1()); } elsif ($arg eq '-son') { $sort_per_size = 1; prtv1( "Set size sort ON.\n" ) if (VERB1()); } elsif ($arg eq '-sup') { $sort_per_size = 1; $sort_decending = 0; # largest FIRST, else smallest first prtv1( "Set size sort ON, ascending.\n" ) if (VERB1()); } elsif ($arg eq '-sdown') { $sort_per_size = 1; $sort_decending = 1; # largest FIRST, else smallest first prtv1( "Set size sort ON, decending.\n" ) if (VERB1()); } elsif ($arg =~ /^-sa/) { $sort_per_size = 0; $sort_alpha = 1; if ($arg eq '-sa') { prtv1( "Set for alpha sort.\n" ); } elsif ($arg eq '-saup') { $sort_decending = 0; prtv1( "Set for alpha sort up - A to Z...\n" ); } elsif ($arg eq '-sadown') { $sort_decending = 1; prtv1( "Set for alpha sort down - Z to A...\n" ); } else { prt( "ERROR: unrecognised SORT ALPHA option? [$arg]! Try -? ... aborting...\n" ); pgm_exit(1,"Bad -s Option"); } } else { prt( "ERROR: unrecognised option? [$arg]! Try -? ... aborting...\n" ); pgm_exit(1,"Bad -s Option"); } } # -x <DIR> or -x @file_list sub deal_with_x_opt($) { my ($arg) = shift; my @arr = split(';',$arg); foreach my $itm (@arr) { if (substr($itm,0,1) eq '@') { # assume an INPUT FILE $itm = substr($itm,1); if (open INF, "<$itm") { my @lns = <INF>; close INF; foreach my $ln (@lns) { chomp $ln; $ln = trim_all($ln); strip_dbl_quotes($ln); if ($ln eq '.') { $got_exclude_root = 1; prtv1("Setting exclude root...\n"); } else { $exclude_dirs{$ln} = 1; prtv1("Setting exclude [$ln]...\n"); } } } else { prt("ERROR: command [$arg]! Unable to open file [$itm]!!\n"); pgm_exit(1,"Command Error"); } } else { if ($itm eq '.') { $got_exclude_root = 1; prtv1("Setting exclude root...\n"); } else { $exclude_dirs{$itm} = 1; prtv1("Setting exclude [$itm]...\n"); } } } } sub give_help() { my $arg = ''; $arg .= " -d - set debug on. Presently ".($dbg_on ? "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 .= " -s[on|off] - Enable or disable size sorting.\n"; $arg .= " -s[up|down] - Enable sorting 'up', or 'down'\n"; $arg .= " -sa[up|down] - Enable alpha sorting 'up', or 'down'\n"; $arg .= " -v[Num} - set verbosity level. Presently [$verbose].\n"; $arg .= " -x <DIR> - Exclude this directory. \@file - line separated list to exclude.\n"; $arg .= " -xd - Exclude sub-directories. Only root files.\n"; $arg .= " -ll - Load log file at end.\n"; $arg .= " input_folder[s] - Directories to process ... recursive assumed.\n"; $arg .= "If none given, abort operation.\n"; prt("$arg\n"); pgm_exit(0,'Help exit'); } sub pre_parse_v_opt { my @av = @_; while (@av) { my $arg = $av[0]; if ($arg =~ /^-v/) { deal_with_v_opt($arg); } shift @av; } } sub parse_arguments { my @av = @_; # take it off the passed stack my ($tmp,$len,$i,$cc); pre_parse_v_opt(@av); while (@av) { my $arg = $av[0]; if ($arg eq '-version') { prt( "Version 0.0.1\n" ); pgm_exit(0,'Version exit'); } elsif (($arg eq '-?')||($arg eq '-h')||($arg eq '--help')) { give_help(); pgm_exit(0,"HelpExit"); } elsif ($arg eq '-i') { require_argument(@av); shift @av; $arg = $av[0]; deal_with_i_opt($arg); } elsif ($arg =~ /^-v/) { # already done - deal_with_v_opt($arg); } elsif ($arg eq '-d') { prtv1( "Setting debug output ...\n" ); $dbg_on = 1; } elsif ($arg eq '-ll') { $load_log = 1; prtv1( "Set load log file at end.\n" ); } elsif ($arg eq '-max') { require_argument(@av); shift @av; $arg = $av[0]; $max_cd = $arg * 1024 * 1024; prtv1( "Setting max CD/DVD to $max_cd (".b2ks2($max_cd).") ...\n" ); } elsif ($arg =~ /^-s/) { deal_with_s_opt($arg); } elsif ($arg eq '-x') { require_argument(@av); shift @av; $arg = $av[0]; deal_with_x_opt($arg); $got_exclude_dirs = scalar keys(%exclude_dirs); prtv1( "Added [$arg] to exclude directory...($got_exclude_dirs)\n" ); } elsif ($arg eq '-xd') { $recursive = 0; prtv1( "Set recursive off. No sub-directories scanned\n" ); } elsif ($arg =~ /^-/) { prt( "ERROR: unrecognised option? [$arg]! Try -? ... aborting...\n" ); pgm_exit(1,"Bad Option"); } else { prtv1( "Storing argument [$arg].\n" ); push(@in_files, $arg); } shift @av; # move to next argument to [0] } $got_exclude_dirs = scalar keys(%exclude_dirs); push(@in_files, $def_file) if (!@in_files && $add_def_if_none); # default to current folder } # eof - dvdsizes.pl