#!/usr/bin/perl -w
use strict;
use warnings;

# globals
use vars qw/ $_TIMER /;

# auto-flush output; no buffering STDOUT
BEGIN { ++$| }

# try to get high-precision time reporting capability
$_TIMER = sub { time };

require Time::HiRes if eval {

   require Time::HiRes;

   $_TIMER = sub { &Time::HiRes::time };

   1;
};

use File::Util;
use Class::OOorNO qw/ shave_opts /;

# initialize variables to use
my($dirs,$files,$exdirsizes,$dirsize,$fcount,$dcount) = (0,0,0,0,0,0);

# use current working directory if no directory name(s) provided
$ARGV[0] = '.' unless @ARGV;

# search for options/flags among input, check input for non-existent file names
for (my($i) = 0; $i < @ARGV; ++$i) {

   if ($ARGV[$i] eq '-h' || $ARGV[$i] eq '--help') {

      print &usage and exit
   }

   if ($ARGV[$i] eq '-e' || $ARGV[$i] eq '--exclude-dirs') {

      splice(@ARGV,$i,1); $exdirsizes = 1;

      # can't use "CORE::last" here because we need to make sure we've
      # checked that every element in @ARGV is not '-h' or '--help'
   }
   elsif (!-e $ARGV[$i]) { die &nosuchfile($ARGV[$i]) }
}

my($f) = File::Util->new();

# calculate directory size
foreach (@ARGV) {

   print <<__PROGRESS__;
Collecting disk usage information for "$_" ...
__PROGRESS__

   my($dir) = $_;

   foreach ($f->list_dir($dir,qw/
      --with-paths
      --no-fsdots
      --follow
      --max-dives=100000000 /)
   ) {

      next unless defined -s $_; # don't know why this is neccessary, but is

      if (-d $_) {

         ++$dcount;

         $dirsize += -s $_ unless $exdirsizes
      }
      else {

         ++$fcount;

         $dirsize += -s $_
      }
   }
}

# print out findings and exit
print &summary() and exit;

# subroutine to display summary of findings
sub summary { <<__SUMMARY__ }

$dirsize bytes in $fcount files and $dcount directories.
   that's ${\ sprintf('%0.2f', $dirsize / 1024) } Kilobytes
          ${\ sprintf('%0.2f', $dirsize / 1048576) } Megabytes
          ${\ sprintf('%0.3f', $dirsize / 1073741824) } Gigabytes
          ${\ sprintf('%0.3f', $dirsize / 1099511627776) } Terabytes

   calculated in ${\ sprintf('%0.2f', (&$_TIMER - $^T)) } seconds

__SUMMARY__

# subroutine to detail program usage
sub usage { <<__USAGE__ }
dirsize, gets the collective disk usage for the contents of a given
directory or list of directories.  Version 1.0.0, 3-Dec-2003.

   usage: dirsize [options] [list of directory names]

   -h --help               print this message
   -e --exclude-dirs       excludes file sizes of the actual directory
                           files that are encountered.  (eg- typically
                           1024 bytes on linux file systems, and reported
                           as 0 bytes on Micro\044oft file systems)
   -y --follow-symlinks    follow symlinks
      ^^^
      !!! Not yet implemented!

   -f --one-fs             stay on one filesystem; don't span partitions or
                           other storage devices
      ^^^
      !!! Not yet implemented!

   If no file names are given, dirsize will assume '.' as the directory for
   disk usage reports.

__USAGE__

# subroutine to report non-existent file errors
sub nosuchfile { <<__NOSUCH__ }
No such file or directory "$_[0]"

__NOSUCH__

__END__

1 Kilobyte = 1,024 bytes
1 Megabyte = 1,048,576 bytes
1 Gigabyte = 1,073,741,824 bytes
1 Terabyte = 1,099,511,627,776 bytes