#!/usr/bin/perl -w #< dirdate.pl - show the latest file dates... # 2019-12-20 - fix PATH separator, per os... # 29/01/2014 - Also show the EARLIEST date # 15/03/2013 - Add -s to skip a directory # 01/08/2012 - Show the absolute directory being processed # 19/06/2012 - Add *.lastbuildstate .tlog to -X MSVC excludes # 10/12/2011 - Get total byte size of scan # 12/11/2011 - Option --norepo, to EXCLUDE all repo folders. # 11/09/2011 - Add option --invert, to invert the time sort, and show earliest (oldest) file # 16/07/2011 - Add .suo and .ncb to the def msvc excludes (and .o for gcc Qt compiles) # 06/06/2011 - Like in Ubuntu, added by-day -d option # 15/03/2011 - copied from Ubuntu bin, but fixed -? exit use strict; use warnings; use File::stat; use File::Basename; # split path ($n,$d,$e) = fileparse($file, qr/\.[^.]*/); use File::Spec; # File::Spec->rel2abs($rel); # we are IN the SLN directory, get ABSOLUTE from RELATIVE use Time::gmtime; my $os = $^O; my $PATH_SEP = "\\"; my $perl_dir = 'C:\GTools\perl'; if ( !($os =~ /Win/i) ) { $perl_dir = '/home/geoff/bin'; $PATH_SEP = "/"; } unshift(@INC, $perl_dir); require 'lib_utils.pl' or die "Unable to load 'lib_utils.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); # user variables my $load_log = 0; my $no_repo = 0; my $my_version = "Version 0.1.1 2019-12-20"; #my $my_version = "Version 0.1.0 2013-03-15"; #my $my_version = "Version 0.0.9 2012-08-01"; #my $my_version = "Version 0.0.8 2012-06-19"; #my $my_version = "Version 0.0.7 2011-12-10"; #my $my_version = "Version 0.0.4 2011-06-06"; my @excluded_exts = (); # qw( .Po .o ); my @excluded_files = (); my @excluded_dirs = (); my @def_msvc_excludes = qw( .obj .dep .dll .res .lib .exe .ilk .pdb .manifest .exp .idb .user .vcproj .dsp .sln .dsw .suo .ncb .old .bak .o .lastbuildstate .tlog ); my @def_msvc_files = qw( Buildlog.htm ); # options my $invert_date = 0; # show earliest - reverse order my $per_day = 0; # show as DAY groups # program variables my $in_folder = ''; my @g_all_files = (); my $last_time = 0; my $last_file = ''; my $earliest_time = time(); my $earliest_file = ''; my $total_bytes = 0; my $total_files = 0; my $total_dirs = 0; my $tail_count = 0; my $repo_cnt = 0; my $repo_last = ''; my $repo_time = 0; my $oldest_repo = ''; my $oldest_time = time(); my @warnings = (); my $verbosity = 0; my $got_big_X = 0; sub VERB1() { return ($verbosity >= 1); } sub VERB2() { return ($verbosity >= 2); } sub VERB5() { return ($verbosity >= 5); } sub VERB9() { return ($verbosity >= 9); } # debug my $dbg_01 = 0; sub process_directory($$); 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" ); } } 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 ($msg) = shift; prt($msg); $msg =~ s/\n$//; push(@warnings,$msg); } sub get_YYYYMMDD_hhmmss_UTC($) { my ($t) = shift; my $tm = gmtime($t); my $m = sprintf( "%04d/%02d/%02d %02d:%02d:%02d", $tm->year() + 1900, $tm->mon() + 1, $tm->mday(), $tm->hour(), $tm->min(), $tm->sec() ); return $m; } # #sub get_YYYYMMDD($) { # my ($t) = shift; # my @f = (localtime($t))[0..5]; # my $m = sprintf("%04d/%02d/%02d", # $f[5] + 1900, $f[4] + 1, $f[3]); # return $m; #} sub mycmp2 { # by 2nd component - time in this case return 1 if (${$a}[1] > ${$b}[1]); return -1 if (${$a}[1] < ${$b}[1]); return 0; } sub mycmp2_invert { # by 2nd component - time in this case return -1 if (${$a}[1] > ${$b}[1]); return 1 if (${$a}[1] < ${$b}[1]); return 0; } sub has_repo_folder($) { my $f = shift; return 1 if ($f =~ /(\\|\/)CVS(\\|\/)/); return 2 if ($f =~ /(\\|\/)\.svn(\\|\/)/); return 3 if ($f =~ /(\\|\/)\.git(\\|\/)/); return 4 if ($f =~ /(\\|\/)\.hg(\\|\/)/); return 0; } sub has_excluded_ext($) { my $file = shift; my ($n,$d,$e) = fileparse($file, qr/\.[^.]*/); my ($ext); foreach $ext (@excluded_exts) { return 1 if ($e eq $ext); } return 0; } sub is_excluded_file($) { my $file = shift; my ($n,$d) = fileparse($file); my ($fil); foreach $fil (@excluded_files) { return 1 if ($n eq $fil); return 1 if ($n =~ /^$fil$/i); } return 0; } sub is_temp_file($) { my $file = shift; my ($n,$d) = fileparse($file); return 0 if ($n =~ /^template/i); return 1 if ($n =~ /^temp/i); return 0; } sub is_excluded_dir($) { my $dir = shift; my $lcd = lc($dir); my ($itm,$lci); foreach $itm (@excluded_dirs) { $lci = lc($itm); return 1 if ($lcd eq $lci); } return 0; } sub process_directory($$) { my ($dir,$lev) = @_; my @dirs = (); if ( opendir( DIR, $dir ) ) { my @files = readdir(DIR); closedir(DIR); # $dir .= '/' if !($dir =~ /(\\|\/)$/); ut_fix_directory(\$dir); my ($file,$ff,$sb,$ir,$got_stat); foreach $file (@files) { next if (($file eq '.')||($file eq '..')); $ff = $dir.$file; if (-f $ff) { $total_files++; $got_stat = 0; if ($sb = stat($ff)) { $total_bytes += $sb->size; $got_stat = 1; } next if (has_excluded_ext($file)); next if (is_excluded_file($file)); next if ($got_big_X && is_temp_file($file)); if ($got_stat) { $ir = has_repo_folder($ff); next if ($ir && $no_repo); push(@g_all_files, [$ff, $sb->mtime, $sb->size, $ir, 0]); if ($sb->mtime > $last_time) { $last_time = $sb->mtime; $last_file = $ff; } if ($sb->mtime < $earliest_time) { $earliest_time = $sb->mtime; $earliest_file = $ff; } if ($ir > 0) { $repo_cnt++; if ($sb->mtime > $repo_time) { $repo_last = $ff; $repo_time = $sb->mtime; } if ($sb->mtime < $oldest_time) { $oldest_time = $sb->mtime; $oldest_repo = $ff; } } } else { prtw("WARNING: Unable to 'stat' [$ff]\n"); } } elsif (-d $ff) { $total_dirs++; push(@dirs,$ff) if (!is_excluded_dir($file)); } } } foreach $dir (@dirs) { process_directory($dir,$lev+1); } } # ($tail_count > 0) && $per_day sub show_on_day_basis($) { my $ra = shift; # \@arr my $cnt = scalar @{$ra}; my ($i,$ff,$tm,$ctm,$val,$key,$num,$min,$len); my ($total,$j,$cnum,$num2); my %perday = (); for ($i = 0; $i < $cnt; $i++) { $ff = ${$ra}[$i][0]; $tm = ${$ra}[$i][1]; $ctm = get_YYYYMMDD($tm); if (!defined $perday{$ctm}) { $perday{$ctm} = []; } $val = $perday{$ctm}; push(@{$val}, [ $ff, $tm ]); } my @arr = sort keys(%perday); $num = 0; $min = 0; $total = 0; foreach $key (@arr) { $num++; last if ($num > $tail_count); $val = $perday{$key}; $cnt = scalar @{$val}; $total += $cnt; $ff = ${$val}[0][0]; $tm = ${$val}[0][1]; $len = length($ff); $min = $len if ($len > $min); if (VERB9()) { #$num = scalar @{$val}; for ($j = 0; $j < $cnt; $j++) { $ff = ${$val}[$j][0]; $tm = ${$val}[$j][1]; $len = length($ff); $min = $len if ($len > $min); } } } $num = scalar @arr; prt("List of $total files, spread over $num days...\n"); if (VERB9()) { $num = 0; foreach $key (@arr) { last if ($num > $tail_count); $num++; $val = $perday{$key}; $cnt = scalar @{$val}; $num2 = 0; for ($j = 0; $j < $cnt; $j++) { $num2++; $ff = ${$val}[$j][0]; $tm = ${$val}[$j][1]; if ($j == 0) { $ctm = get_YYYYMMDD($tm); prt("Count of $cnt for day $ctm\n"); } $ctm = get_YYYYMMDD_hhmmss_UTC($tm); # for display $cnum = sprintf("%4d",$num2); $ff .= ' ' while (length($ff) < $min); prt("$cnum: $ff $ctm\n"); } } $num = scalar @arr; prt("\nRepeated list of $total files, spread over $num days... showing only first...\n"); } $num = 0; foreach $key (@arr) { $num++; last if ($num > $tail_count); $val = $perday{$key}; $cnt = scalar @{$val}; $ff = ${$val}[0][0]; $tm = ${$val}[0][1]; # for display $ff .= ' ' while (length($ff) < $min); $cnt = ' '.$cnt while (length($cnt) < 6); $cnum = sprintf("%4d",$num); prt("$num: $ff ".get_YYYYMMDD_hhmmss_UTC($tm)." $cnt\n"); } } sub show_last() { my @arr = (); if ($invert_date) { @arr = sort mycmp2_invert @g_all_files; } else { @arr = sort mycmp2 @g_all_files; } my $cnt = scalar @arr; my ($i,$ff,$tm,$num,$min,$len,$msg,$cnum,$ok); $min = 0; $ok = 0; if ($invert_date) { if ($cnt && length($earliest_file) && ($earliest_time < time())) { $len = length($earliest_file); $min = $len if ($len > $min); $ok = 1; } if (length($oldest_repo) && ($oldest_time > 0) && ($oldest_repo ne $earliest_file) ) { $len = length($oldest_repo); $min = $len if ($len > $min); } } else { if ($cnt && length($last_file) && ($last_time > 0)) { $len = length($last_file); $min = $len if ($len > $min); $ok = 1; } if (length($repo_last) && ($repo_time > 0) && ($repo_last ne $last_file) ) { $len = length($repo_last); $min = $len if ($len > $min); } } if ($ok) { $msg = $earliest_file; $msg .= ' ' while (length($msg) < $min); prt("Oldest : $msg ".get_YYYYMMDD_hhmmss_UTC($earliest_time).", of $cnt files.\n"); $msg = $last_file; $msg .= ' ' while (length($msg) < $min); prt("Latest : $msg ".get_YYYYMMDD_hhmmss_UTC($last_time).", of $cnt files.\n"); if ($invert_date) { if ( length($oldest_repo) && ($oldest_time < time()) && ($oldest_repo ne $earliest_file) ) { $msg = $oldest_repo; $msg .= ' ' while (length($msg) < $min); prt("Repo Old : $msg ".get_YYYYMMDD_hhmmss_UTC($oldest_time).", of $repo_cnt repo files.\n"); } } else { if ( length($repo_last) && ($repo_time > 0) && ($repo_last ne $last_file) ) { $msg = $repo_last; $msg .= ' ' while (length($msg) < $min); prt("Repo Last: $msg ".get_YYYYMMDD_hhmmss_UTC($repo_time).", of $repo_cnt repo files.\n"); } } if ($tail_count > 0) { if ($per_day) { show_on_day_basis(\@arr); } else { $num = $cnt; $min = 0; for ($i = 0; $i < $cnt; $i++) { if ($num <= $tail_count) { $ff = $arr[$i][0]; $len = length($ff); $min = $len if ($len > $min); } $num--; } $num = $cnt; for ($i = 0; $i < $cnt; $i++) { if ($num <= $tail_count) { $ff = $arr[$i][0]; $tm = $arr[$i][1]; $ff .= ' ' while (length($ff) < $min); $cnum = sprintf("%4d",$num); prt("$cnum: $ff ".get_YYYYMMDD_hhmmss_UTC($tm)."\n"); } $num--; } } } pgm_exit(0,""); } else { prt("No files found in [$in_folder]!\n"); } } # ### MAIN ### parse_args(@ARGV); prt("Processing in folder [$in_folder]\n"); process_directory($in_folder,0); prt("Processed $total_dirs directories, $total_files files, ".get_nn($total_bytes)." bytes.\n"); show_last(); pgm_exit(0,""); # ### end ### sub need_arg { my ($arg,@av) = @_; if (!@av) { prt("ERROR: Argument [$arg] must be followed by another argument!\n"); pmg_exit(1,""); } } sub parse_args { my @av = @_; my $cnt = scalar @av; prt("Parsing $cnt aruments...\n") if ($dbg_01 || VERB5()); my ($sarg,@arr,$msg); my $cmd = ''; while (@av) { my $arg = $av[0]; $cmd .= "$arg "; if ($arg =~ /^-/) { $sarg = substr($arg,1); $sarg = substr($sarg,1) while ($sarg =~ /^-/); if (($sarg eq '?')||($sarg =~ /^h/i)) { give_help(); exit(0); } elsif ($sarg =~ /^i/) { $invert_date = 1; prt("Invert date - show oldest last.\n") if (VERB2()); } elsif ($sarg =~ /^d/) { $per_day = 1; prt("Set the show tail on a per day basis.\n") if (VERB2()); } elsif ($sarg =~ /^e/) { need_arg(@av); shift @av; $sarg = $av[0]; push(@excluded_files,$sarg); prt("Set to EXCLUDE file [$sarg]\n"); } elsif ($sarg =~ /^l/) { set_load_log($sarg,\$load_log); prt("Set to load log at end.\n") if (VERB2()); } elsif ($sarg =~ /^t/) { need_arg(@av); shift @av; $sarg = $av[0]; $cmd .= "$sarg "; if ($sarg =~ /^\d+$/) { $tail_count = $sarg; prt("set tail count to [$tail_count]\n") if (VERB2()); # if ($dbg_01); } else { prt("ERROR: Argument [$arg] MUST be followed by a numeric count!\n"); exit(1); } } elsif ($sarg =~ /^v/) { if ($sarg =~ /^v(\d+)$/) { $verbosity = $1; } else { while ($sarg =~ /^v/) { $verbosity++; $sarg = substr($sarg,1); } } prt("Set verbosity to [$verbosity]\n") if (VERB2()); } elsif ($sarg =~ /^x/) { need_arg(@av); shift @av; $sarg = $av[0]; $cmd .= "$sarg "; @arr = split(':',$sarg); foreach $sarg (@arr) { $sarg = '.'.$sarg if ( !($sarg =~ /^\./) ); push(@excluded_exts,$sarg); prt("Excluding extension [$sarg].\n") if (VERB2()); } } elsif ($sarg =~ /^s/) { need_arg(@av); shift @av; $sarg = $av[0]; $cmd .= "$sarg "; push(@excluded_dirs,$sarg); prt("Excluding directory [$sarg].\n") if (VERB2()); } elsif ($sarg =~ /^X/) { $got_big_X = 1; $msg = "$arg excludes ["; foreach $sarg (@def_msvc_excludes) { push(@excluded_exts,$sarg); $msg .= "$sarg "; } $msg =~ s/\s+$//; $msg .= "]\n"; $msg .= "$arg and files ["; foreach $sarg (@def_msvc_files) { push(@excluded_files,$sarg); $msg .= "$sarg"; } $msg =~ s/\s+$//; $msg .= "]\n"; prt($msg) if (VERB2()); } elsif ($sarg =~ /^n/) { $no_repo = 1; prt("Exclude all repo folders. (.git/.svn/CVS/.hg)\n") if (VERB2()); } else { prt("ERROR: Unknown argument [$arg]! Aborting...\n"); pgm_exit(1,""); } } else { $in_folder = File::Spec->rel2abs($arg); prt("Set input folder to [$in_folder] [$arg]\n") if ($dbg_01 || VERB2()); } shift @av; } if (length($in_folder) == 0) { prt("ERROR: No input folder found in command...[$cmd]!\n"); pgm_exit(1,""); } } sub give_help { prt("$pgmname $my_version, in OS [$os]\n"); prt("Usage: [options] input-directory\n"); prt("Options:\n"); prt(" --help (-h or -?) = This help, and exit 0\n"); prt(" --invert (-i) = Invert sort and show OLDEST file.\n"); prt(" --load (-l) = Load log at end.\n"); prt(" --tail (-t) = Show of the latest files.\n"); prt(" --day (-d) = Show tail using the 'day' as the criteria.\n"); prt(" --norepo (-n) = Exclude ALL repo folders. (.git/.svn/CVS/.hg)\n"); prt(" --verb[nn] (-v) = Bump [set] verbosity. def=$verbosity\n"); prt(" --xclude (-x) = Exclude files with this extension. Can be a ':' sep list.\n"); prt(" --exclud (-e) = Exclude a specific file.\n"); prt(" --skip (-s) = Skip this directory, and all children.\n"); prt("A special -X will exclude most files built.\n"); prt("The default is to ONLY show the latest file found.\n"); prt("And the latest in any 'repo' folder found.\n"); } # eof