jtouchdir (Source)

#!/usr/bin/perl -w
##---------------------------------------------------------------------------##
#
#   Program: jtouchdir
#   Author:  Brian <genius@groupbcl.ca> :)
#   Date:    1 January 2010
#
#   Sets the mtime on a directory based on its contents.
#
#   For each directory name given, gets a lists of files in the directory, 
#   orders them according to extension, determines the most common extension, 
#   determines the timestamp of the most recently modified file of all the
#   files that share that extension, then sets the modification timestamp of
#   the directory to that time.
#
#   For example, running 'jtouchdir' on a directory containing mostly JPEG
#   files, the timestamp on the directory itself will be set to the same
#   value as the most recent JPEG file in the directory.
#
##---------------------------------------------------------------------------##
#   January 2010    Initial Version
#   April 2012      Added -r (recursive)
##---------------------------------------------------------------------------##
# BUUS: This script is part of Brian's Useful Utilities Set
use strict;

die "usage: jtouchdir [-r] directory [directory ...]\n" if ! $ARGV[0];

my $recursive = 0;
$recursive=1, shift if $ARGV[0] eq '-r';

foreach my $dn (@ARGV) {
    warn "$dn: not a directory",  next if ! -d $dn;
    warn "$dn: cannot read", next if ! -r $dn;
    touch_dir($dn);
}

sub touch_dir {
    my $dn = shift;
    my @subdir_list;
    my %exts;   # Key = extension data = aref:
                #   [0]=counter, [1]=highest mtime. [2]=file with that mtime
    my ($hcount, $hcount_ext) = (0, '');

    my $dh;
    opendir $dh,  $dn or die "Unable to open directory '$dn' for reading: $!";
    while (my $fn = readdir $dh) {
        next if $fn eq '.' || $fn eq '..';
        next if ! -f "$dn/$fn" && ! -d "$dn/$fn";   # Consider only files and sub-dirs

        my $is_dir = -d "$dn/$fn" ? 1 : 0;

        my $ext;
        if ($is_dir) {
            $ext = '__DIR__';
        } else {
            $ext = '__NONE__';
            $ext = $1 if ! $is_dir && $fn =~ /\.([^\.]+)$/;
        }

        touch_dir("$dn/$fn") if $is_dir && $recursive;
        my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime,
                    $mtime, $ctime, $blksize, $blocks) = stat("$dn/$fn");

        if (exists $exts{$ext}) {
            $exts{$ext}[0]++;
            $exts{$ext}[1] = $mtime, $exts{$ext}[2] = $fn if $mtime > $exts{$ext}[1];
        } else {
            $exts{$ext} = [ 1, $mtime, $fn ]
        }

        $hcount_ext = $ext, $hcount = $exts{$ext}[0] if $exts{$ext}[0] > $hcount;
#       print "  $fn (mtime=$mtime) --> $ext: $exts{$ext}[0], $exts{$ext}[1] | hcount=$hcount, hcount_ext=$hcount_ext\n";
    }
    closedir $dh;

    if ($hcount_ext) {
        my $fn = $exts{$hcount_ext}[2];
        print "$dn: touching directory based on ", -d "$dn/$fn" ? 'subdirectory' : 'file', " '$fn'\n";
        utime $exts{$hcount_ext}[1], $exts{$hcount_ext}[1], $dn;
    } else {
        print "$dn: unable to determine most recent common file (is directory empty?)\n";
    }
}

# vim: tabstop=4