#!/usr/bin/perl -w
use strict;
use File::Copy;
use Term::ReadLine;
use Time::Local;

###########################################################
# AutoScrapbook 4.1 (July 3, 2003)
# (Copyright 2003, Kirk Bauer) (see license at end of file)
###########################################################
# This program was written by Kirk Bauer <kirk@kaybee.org>
# 
# It is distributed under the MIT license, which basically
# says that you can do anything you want with this problem
# (even commercial activities) for free, as long as you 
# give me credit for the program.
#
# For more information, please visit http://linux.kaybee.org/
###########################################################

###########################################################
# Put your local values here...
###########################################################
my $YourName = 'Kirk Bauer';
my $YourEmail = 'kirk@kaybee.org';

# Set this to 1 to ask for file renaming, deletion, and rotation
my $Renames = 1;

# Set this to 1 to ask before re-processing a directory
my $AskReprocess = 0;

# Set this to 1 to enable debugging
my $Debug = 0;
###########################################################

###########################################################
# Customize external program locations and arguments here
###########################################################
my $IDENTIFY = "identify";
my $MAKE_THUMBNAIL = "convert -geometry 100x100+0+0";

# (note: set $XV to '' to disable picture display)
my $XV = "xv -wait 10 -ge \"400x300+10+10\"";

# Set to '' to disable reading of EXIF information
my $EXIF = "/usr/bin/exif";

# Will be used instead of exif when applicable
my $EXIFTAGS = "/usr/bin/exiftags";

# Use jpegtran if you have it, otherwise convert will work
my $ROTATE = "jpegtran -copy all -rotate";
#my $ROTATE = "convert -quality 85 -rotate";

# Set these if you want to make medium-sized images (otherwise set to 0)
my $MED_SIZE_CUTOFF = 350;      # Create a medium-sized image when over 350kb
my $MAKE_MED = "convert -geometry 640x640+0+0";

# Set these if you want to make large-sized images (otherwise set to 0)
my $LG_SIZE_CUTOFF = 1000;      # Create a large-sized image when over 1000kb
my $MAKE_LG = "convert -geometry 1280x1280+0+0";

# Do you want to use EXIF thumbnails if available?
my $EXIFthumbs = 1;

# If you have the 'jhead' program, picture descriptions will
# be stored within JPEGs instead of in a separate file.  If
# you enable this, you can only edit these descriptions by
# using the command 'jhead -ce picture.jpg' and then re-
# generating the HTML for that directory.  Be sure to leave
# the "AUTOSCRAPBOOK" line in there somewhere.
my $JHEAD = "/usr/bin/jhead";
###########################################################

my (%EXIFData, @EXIFAlways);
###########################################################
# Display these EXIF tags by default
###########################################################
@EXIFAlways = (
   # These are from the 'exif' program
   'Aperture',
   # These are from the 'exiftags' program
   'F-Number',
   'Camera Model',
   'ISO Speed Rating',
   # These are from both programs
   'Exposure Time',
   'Flash',
   );

###########################################################
# All Possible EXIF Tags (comment out items to ignore)
# Only matters if you are *not* using exiftags (only using exif)
###########################################################
#$EXIFData{'0x0001'} = 'InteroperabilityIndex';
#$EXIFData{'0x0002'} = 'InteroperabilityVersion';
$EXIFData{'0x0100'} = 'Image Width';
$EXIFData{'0x0101'} = 'Image Length';
#$EXIFData{'0x0102'} = 'Bits per Sample';
#$EXIFData{'0x0103'} = 'Compression';
$EXIFData{'0x0106'} = 'Photometric Interpretation';
$EXIFData{'0x010a'} = 'Fill Order';
$EXIFData{'0x010d'} = 'Document Name';
$EXIFData{'0x010e'} = 'Image Description';
$EXIFData{'0x010f'} = 'Manufacturer';
$EXIFData{'0x0110'} = 'Model';
#$EXIFData{'0x0111'} = 'Strip Offsets';
#$EXIFData{'0x0112'} = 'Orientation';
#$EXIFData{'0x0115'} = 'Samples per Pixel';
#$EXIFData{'0x0116'} = 'Rows per Strip';
#$EXIFData{'0x0117'} = 'Strip Byte Count';
#$EXIFData{'0x011a'} = 'x-Resolution';
#$EXIFData{'0x011b'} = 'y-Resolution';
#$EXIFData{'0x011c'} = 'Planar Configuration';
#$EXIFData{'0x0128'} = 'Resolution Unit';
$EXIFData{'0x012d'} = 'Transfer Function';
$EXIFData{'0x0131'} = 'Software';
$EXIFData{'0x0132'} = 'Date and Time';
$EXIFData{'0x013b'} = 'Artist';
$EXIFData{'0x013e'} = 'White Point';
$EXIFData{'0x013f'} = 'Primary Chromaticities';
$EXIFData{'0x0156'} = 'Transfer Range';
$EXIFData{'0x0200'} = 'JPEGProc';
$EXIFData{'0x0201'} = 'JPEG Interchange Format';
$EXIFData{'0x0202'} = 'JPEG Interchange Format Lengt';
#$EXIFData{'0x0211'} = 'YCbCr Coefficients';
#$EXIFData{'0x0212'} = 'YCbCr Sub-Sampling';
#$EXIFData{'0x0213'} = 'YCbCr Positioning';
$EXIFData{'0x0214'} = 'Reference Black/White';
$EXIFData{'0x1000'} = 'RelatedImageFileFormat';
$EXIFData{'0x1001'} = 'RelatedImageWidth';
$EXIFData{'0x1002'} = 'RelatedImageLength';
$EXIFData{'0x828d'} = 'CFARepeatPatternDim';
$EXIFData{'0x828e'} = 'CFA Pattern';
$EXIFData{'0x828f'} = 'Battery Level';
$EXIFData{'0x8298'} = 'Copyright';
$EXIFData{'0x829a'} = 'Exposure Time';
$EXIFData{'0x829d'} = 'FNumber';
$EXIFData{'0x83bb'} = 'IPTC/NAA';
$EXIFData{'0x8769'} = 'ExifIFDPointer';
$EXIFData{'0x8773'} = 'InterColorProfile';
$EXIFData{'0x8822'} = 'ExposureProgram';
$EXIFData{'0x8824'} = 'Spectral Sensitivity';
$EXIFData{'0x8825'} = 'GPSInfoIFDPointer';
$EXIFData{'0x8827'} = 'ISO Speed Ratings';
$EXIFData{'0x8828'} = 'OECF';
#$EXIFData{'0x9000'} = 'Exif Version';
#$EXIFData{'0x9003'} = 'Date and Time (original)';
#$EXIFData{'0x9004'} = 'Date and Time (digitized)';
#$EXIFData{'0x9101'} = 'ComponentsConfiguration';
#$EXIFData{'0x9102'} = 'Compressed Bits per Pixel';
$EXIFData{'0x9201'} = 'Shutter speed';
$EXIFData{'0x9202'} = 'Aperture';
$EXIFData{'0x9203'} = 'Brightness';
$EXIFData{'0x9204'} = 'Exposure Bias';
#$EXIFData{'0x9205'} = 'MaxApertureValue';
$EXIFData{'0x9206'} = 'Subject Distance';
$EXIFData{'0x9207'} = 'Metering Mode';
$EXIFData{'0x9208'} = 'Light Source';
$EXIFData{'0x9209'} = 'Flash';
$EXIFData{'0x920a'} = 'Focal Length';
$EXIFData{'0x9214'} = 'Subject Area';
#$EXIFData{'0x927c'} = 'Maker Note';
$EXIFData{'0x9286'} = 'User Comment';
$EXIFData{'0x9290'} = 'SubsecTime';
$EXIFData{'0x9291'} = 'SubSecTimeOriginal';
$EXIFData{'0x9292'} = 'SubSecTimeDigitized';
#$EXIFData{'0xa000'} = 'FlashPixVersion';
$EXIFData{'0xa001'} = 'Color Space';
#$EXIFData{'0xa002'} = 'PixelXDimension';
#$EXIFData{'0xa003'} = 'PixelYDimension';
$EXIFData{'0xa004'} = 'RelatedSoundFile';
#$EXIFData{'0xa005'} = 'InteroperabilityIFDPointer';
$EXIFData{'0xa20b'} = 'Flash Energy';
$EXIFData{'0xa20c'} = 'Spatial Frequency Response';
#$EXIFData{'0xa20e'} = 'Focal Plane x-Resolution';
#$EXIFData{'0xa20f'} = 'Focal Plane y-Resolution';
#$EXIFData{'0xa210'} = 'Focal Plane Resolution Unit';
$EXIFData{'0xa214'} = 'Subject Location';
$EXIFData{'0xa215'} = 'Exposure index';
$EXIFData{'0xa217'} = 'Sensing Method';
#$EXIFData{'0xa300'} = 'File Source';
$EXIFData{'0xa301'} = 'Scene Type';
$EXIFData{'0xa302'} = 'CFA Pattern';
$EXIFData{'0xa401'} = 'Custom Rendered';
$EXIFData{'0xa402'} = 'Exposure Mode';
$EXIFData{'0xa403'} = 'White Balance';
$EXIFData{'0xa404'} = 'Digital Zoom Ratio';
$EXIFData{'0xa405'} = 'Focal Length In 35mm Film';
$EXIFData{'0xa406'} = 'Scene Capture Type';
$EXIFData{'0xa407'} = 'Gain Control';
$EXIFData{'0xa408'} = 'Contrast';
$EXIFData{'0xa409'} = 'Saturation';
$EXIFData{'0xa40a'} = 'Sharpness';
$EXIFData{'0xa40b'} = 'Device Setting Description';
$EXIFData{'0xa40c'} = 'Subject Distance Range';
$EXIFData{'0xa420'} = 'Image Unique ID';
###########################################################

sub CleanJpg($);

# See if we have the exif program...
unless (-x $EXIF) {
   $EXIF = '';
}

# See if we have the jhead program...
unless (-x $JHEAD) {
   $JHEAD = '';
}

my $pmtime = (stat $0)[9];
my %Renamed;

my $term = new Term::ReadLine 'AutoScrapbook';

sub GetInput {
   my $input;
   unless ($input = $term->readline('> ')) {
      return '';
   }
   $input =~ s/^\s+//;
   $input =~ s/\s+$//;
   return ($input);
}

sub GetResolution ($) {
   my $file = $_[0];
   my $res = `$IDENTIFY "$file"`;
   chomp($res);
   $res =~ s/^.*?(\d+x\d+).*$/$1/;
   return $res;
}

# Takes in time of form '2002:01:20 07:19:28'
sub FormatEXIFTimeEpoch ($) {
   my ($year, $mon, $mday, $hour, $min, $sec) = split /:| /, $_[0];
   return(timelocal($sec, $min, $hour, $mday, $mon-1,$year-1900));
}

# Takes in time of form '2002:01:20 07:19:28'
sub FormatEXIFTime ($) {
   my ($year, $mon, $mday, $hour, $min, $sec) = split /:| /, $_[0];
   my $month = ('NONE', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')[$mon];
   my $ampm = 'AM';
   if ($hour == 12) {
      $ampm = 'PM';
   }
   if ($hour > 12) {
      $ampm = 'PM';
      $hour -= 12;
   }
   $hour =~ s/^0//;
   unless ($min =~ /\d\d/) {
      $min = "0$min";
   }
   my $newtime = "$month $mday, $year ($hour:$min $ampm)";
   return ($newtime);
}

sub FormatTime ($) {
   my $time = $_[0];
   my ($sec, $min, $hour, $mday, $mon, $year) = localtime($time);
   my $month = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')[$mon];
   $year = $year + 1900;
   my $ampm = 'AM';
   if ($hour == 12) {
      $ampm = 'PM';
   }
   if ($hour > 12) {
      $ampm = 'PM';
      $hour -= 12;
   }
   unless ($min =~ /\d\d/) {
      $min = "0$min";
   }
   my $newtime = "$month $mday, $year ($hour:$min $ampm)";
   return ($newtime);
}

sub ImportDescription($$$) {
   my ($Dir, $This, $temp) = @_;
   return unless $JHEAD;
   return unless (system("echo AUTOSCRAPBOOK > $Dir/.$temp.jhead && /bin/cat $Dir/.$temp >> $Dir/.$temp.jhead") == 0);
   my $mtime = (stat("$Dir/$This"))[9];
   $ENV{'EDITOR'} = "/bin/cp $Dir/.$temp.jhead";
   if (system("$JHEAD -ce -ft $Dir/$This >/dev/null 2>&1") == 0) {
      # Delete the file if jhead was sucessful
      unlink "$Dir/.$temp";
      chmod (0644, "$Dir/$This");
      print "Description for $This stored in JPEG header...\n";
      my $newmtime = (stat("$Dir/$This"))[9];
      if ($newmtime > $mtime) {
         # Apparently the image did *not* have an EXIF time and this process just
         # changed its timestamp, so change it back
         utime $mtime, $mtime, "$Dir/$This";
      }
   }
   unlink "$Dir/.$temp.jhead";
}

sub GenerateSmallerImage ($$$$$$$$$) {
   my ($Dir, $This, $origsize, $mtime, $cutoff, $command, $prefix, $sizes, $clean) = @_;
   my $sizeschanged = 0;
   my $size = 0;
   my $res = 0;
   if ($cutoff and ($origsize > $cutoff)) {
      my $mtime2 = 0;
      # Generate smaller-sized image
      if (-f "$Dir/.thumbs/$prefix-$This") {
         $mtime2 = (stat "$Dir/.thumbs/$prefix-$This")[9];
      }
      if ($mtime > $mtime2) {
         system "$command \"$Dir/$This\" \"$Dir/.thumbs/$prefix-$This\"" and die "Could not make smaller image of $This ($command)\n";
         if ($clean) {
            CleanJpg("$Dir/.thumbs/$prefix-$This");
         }
         chmod (0644, "$Dir/.thumbs/$prefix-$This");
         print "Created $prefix image for picture: $This\n";
         $res = GetResolution("$Dir/.thumbs/$prefix-$This");
      }

      $size = (stat "$Dir/.thumbs/$prefix-$This")[7];
      $size = int($size/1000);
      if ($sizes->{"$prefix-$This"}) {
         if ($res) {
            # Just generated
            if ($res ne $sizes->{"$prefix-$This"}) {
               $sizes->{"$prefix-$This"} = $res;
               $sizeschanged++;
            }
         } else {
            $res = $sizes->{"$prefix-$This"};
         }
      } else {
         unless ($res) {
            $res = GetResolution("$Dir/.thumbs/$prefix-$This");
         }
         $sizes->{"$prefix-$This"} = $res;
         $sizeschanged++;
      }
   }
   return ($sizeschanged, $res, $size);
}

sub GeneratePage ($@) {
   my ($Dir, @items) = @_;
   print "Generating page in $Dir\n";
   my ($Name, $Desc, $SubName, $SubDesc, $SubNum, $mtime, $mtime2, $This, $Num, $SubNewest, $Newest, $PicDesc, $NewName, $PrevName);
   $Num = 0;
   open (OUT, ">$Dir/index.html.new");
   open (NAME, "$Dir/.name");
   $Name = <NAME>;
   chomp $Name;
   close (NAME);
   open (DESC, "$Dir/.info");
   $Desc = <DESC>;
   chomp $Desc;
   close (DESC);
   print OUT "<HTML>\n <HEAD>\n  <TITLE>$Name</TITLE>\n </HEAD>\n";
   print OUT " <BODY bgcolor=#D0D0D0>\n  <CENTER><H1>$Name</H1></CENTER>\n";
   if (-f "$Dir/.header") {
      open (HEADER, "$Dir/.header");
      foreach (<HEADER>) {
         print OUT $_;
      }
      close (HEADER);
   }
   if ($Desc) {
      print OUT "<HR><CENTER><P>$Desc</P></CENTER><HR><BR>\n";
   }
   $Newest = 0;
   if (grep /\/$/, @items) {
      # There are subdirectories
      print OUT "  <TABLE border=5 cellspacing=3 cellpadding=5 width=95% align=center>\n";
      print OUT "   <TR bgcolor=#5050F0><TH>Sub-Categories</TH><TH># Pics</TH><TH>Newest Picture</TH></TR>\n";
      foreach $This (@items) {
         if ($This =~ /\/$/) {
            open (NAME, "$Dir/$This/.name");
            $SubName = <NAME>;
            chomp $SubName;
            close (NAME);
            open (DESC, "$Dir/$This/.info");
            $SubDesc = <DESC>;
            chomp $SubDesc;
            close (DESC);
            if (open (NUM, "$Dir/$This/.num")) {
               $SubNum = <NUM>;
               chomp $SubNum;
               close (NUM);
            } else {
               $SubNum = 0;
            }
            open (NEWEST, "$Dir/$This/.newest");
            $SubNewest = <NEWEST>;
            chomp $SubNewest;
            close (NEWEST);
            $Num += $SubNum;
            if ($Newest < $SubNewest) {
               $Newest = $SubNewest;
            }
            my $temp = FormatTime ($SubNewest);
            if ($SubDesc) {
               $SubDesc = "(<I>$SubDesc</I>)";
            }
            print OUT "   <TR> <TD> <A HREF=$This>$SubName</A> $SubDesc </TD> <TD>$SubNum</TD> <TD>$temp</TD></TR>\n"; 
         }
      }
      print OUT "  </TABLE>\n";
   }
   if (grep /[^\/]$/, @items) {
      # There are images...
      # Get cached image sizes
      my ($sizes, $szmtime) = GetImageSizes($Dir);
      my $sizeschanged = 0;
      print OUT "  <P><TABLE border=5 cellpadding=5 cellspacing=3 width=95% align=center>\n";
      print OUT "   <TR bgcolor=#5050F0><TH>Picture</TH><TH>Description</TH><TH>Date/Time</TH>\n";
      if ($EXIF) {
         print OUT "   <TH>EXIF Data</TH>\n";
      }
      print OUT "</TR>\n";
      foreach $This (@items) {
         if ($This =~ /[^\/]$/) {
            my %PictureData;
            my $size;
            (undef, undef, undef, undef, undef, undef, undef, $size, undef, $mtime) = stat "$Dir/$This";
            $size = int($size/1000);
            my $res;
            if (($sizes->{$This}) and ($mtime < $szmtime)) {
               $res = $sizes->{$This};
            } else {
               $res = GetResolution("$Dir/$This");
               $sizes->{$This} = $res;
               $sizeschanged++;
            }
            if ($Newest < $mtime) {
               $Newest = $mtime;
            }
            $Num++;
            my $temp = $This;
            $temp =~ s/\.[^\.]+$//;
            $PicDesc = '';
            if (-f "$Dir/.$temp") {
               open (DESC, "$Dir/.$temp");
               $PicDesc = <DESC>;
               chomp $PicDesc;
               close (DESC);
               unless ($PicDesc) {
                  # Handle empty descriptions...
                  $PicDesc = ' ';
               }
               if ($JHEAD and ($This =~ /(jpg|jpeg)$/i)) {
                  # See if we can import this old-style description
                  ImportDescription($Dir, $This, $temp);
               }
            } elsif ($JHEAD) {
               my $mtime = (stat("$Dir/$This"))[9];
               # See if the description is stored in the JPEG header
               $ENV{'EDITOR'} = '/bin/cat';
               $PicDesc = `$JHEAD -ce $Dir/$This 2>/dev/null`;
               if ($? == 0) {
                  $PicDesc =~ s/Comment not modified//;
                  if ($PicDesc =~ s/Modified: \S+//) {
                     utime $mtime, $mtime, "$Dir/$This";
                  }
                  chomp($PicDesc);
                  if ($PicDesc =~ /Not JPEG/) {
                     # No comment found...
                     $PicDesc = '';
                  } elsif ($PicDesc and ($PicDesc !~ s/AUTOSCRAPBOOK//)) {
                     # Did not find a valid AutoScrapbook description
                     print "\nImage $This has a description not set by AutoScrapbook:\n";
                     print $PicDesc;
                     $PicDesc = '';
                  } else {
                     # Remove old-style description file if it exists
                     unlink "$Dir/.$temp";
                  }
               } else {
                  $PicDesc = '';
               }
            }
            unless ($PicDesc) {
               my $OldName;
               if ($XV) {
                  system ("$XV \"$Dir/$This\" &");
               }
               if ($Renames) {
                  my $ext = $This;
                  $ext =~ s/^.+\.([^\.]+)$/$1/;
                  print "\nDo you want to rename $This?\n";
                  print "Type in a new name or ENTER to keep current name (type 'DELETE' to delete)\n";
                  print "You can also type +90, +180, etc to rotate right (can still be followed by new name)\n";
                  $NewName = GetInput;
                  if ($NewName =~ s/^\+(\d+)\s*//) {
                     my $amount = $1;
                     # Specified a rotation amount
                     my $cmd = "$ROTATE $amount '$Dir/$This' ";
                     if ($ROTATE =~ /jpegtran/) {
                        $cmd .= '> ';
                     }
                     my $mtime = (stat("$Dir/$This"))[9];
                     $cmd .= "'$Dir/$This.rotated.$ext'";
                     if (system($cmd) == 0) {
                        move ("$Dir/$This.rotated.$ext", "$Dir/$This");
                        if ($EXIF and $EXIFthumbs and (system("$EXIF -e '$Dir/$This' >/dev/null 2>&1") == 0)) {
                           # Rotate the thumbnail too
                           if (system("convert -rotate $amount '$Dir/$This.modified.jpeg' '$Dir/$This.thumb.$ext'") == 0) {
                              if (system("$EXIF -n '$Dir/$This.thumb.$ext' '$Dir/$This' >/dev/null 2>&1") == 0) {
                                 move ("$Dir/$This.modified.jpeg", "$Dir/$This");
                                 unlink "$Dir/$This.thumb.$ext";
                              }
                           }
                        }
                        utime $mtime, $mtime, "$Dir/$This";
                        chmod (0644, "$Dir/$This");
                        unlink "$Dir/.thumbs/$This";
                        unlink "$Dir/.thumbs/med-$This";
                        unlink "$Dir/.thumbs/large-$This";
                        print "Rotated $This by $amount.\n";
                     } else {
                        die "Rotation failed: $!\n";
                     }
                  }
                  $OldName = $This;
                  if ($NewName) {
                     if ($NewName eq 's') {
                        # skip
                        next;
                     }
                     if ($NewName eq 'DELETE') {
                        unlink ("$Dir/$This");
                        next;
                     }
                     unless ($NewName =~ /\.[^\.]+$/) {
                        $NewName .= ('.' . lc($ext));
                     }
                     if (-f "$Dir/$NewName") {
                        die "$NewName already exists!\n"
                     }
                     $This = $NewName;
                     $temp = $This;
                     $temp =~ s/\.[^\.]+$//;
                     print "Will rename $OldName to $NewName (hit CTRL-C to abort!!)\n";
                     $Renamed{$NewName} = $OldName;
                  }
               }
               print "\nGive a description for picture $This:\n";
               $PicDesc = GetInput;
               unless ($PicDesc) {
                  # Empty description
                  $PicDesc = ' ';
               }
               open (FILE, ">$Dir/.$temp");
               print FILE "$PicDesc\n";
               close (FILE);
               if ($Renames and $NewName) {
                  move ("$Dir/$OldName", "$Dir/$NewName");
               }
               ImportDescription($Dir, $This, $temp);
            }
            my $EXIFThumbnail = 0;
            if ($EXIFTAGS) {
               foreach my $line (`$EXIFTAGS -q $Dir/$This 2>/dev/null`) {
                  chomp $line;
                  $line =~ s/^\s+//;
                  $line =~ s/\s+$//;
                  my ($tag, $value) = split /:\s+/, $line;
                  if ($value) {
                     if ($tag eq 'Image Created') {
                        $tag = 'Date and Time';
                     }
                     $PictureData{$tag} = $value;
                  }
               }
            }
            if ($EXIF and ((not $EXIFTAGS) or $EXIFthumbs)) {
               # Determine EXIF data
               foreach my $line (`$EXIF -i $Dir/$This 2>/dev/null`) {
                  chomp $line;
                  if (($line =~ /^0x/) and (not $EXIFTAGS)) {
                     my ($tag, $value) = split /\|/, $line;
                     $value =~ s/^\s+//;
                     $value =~ s/\s+$//;
                     if ($value and $EXIFData{$tag}) {
                        $PictureData{$EXIFData{$tag}} = $value;
                     }
                  } elsif ($line =~ /^EXIF data contains a thumbnail/) {
                     $EXIFThumbnail = 1;
                  }
               }
            }
            if ($JHEAD) {
               # If available, set file timestamp to EXIF timestamp
               system("jhead -ft '$Dir/$This' >/dev/null 2>&1");
            }
            unless (-d "$Dir/.thumbs") {
               mkdir ("$Dir/.thumbs", 0755);
               chmod (0755, "$Dir/.thumbs");
            }
            # Generate standard thumbnail
            if (-f "$Dir/.thumbs/$This") {
               $mtime2 = (stat "$Dir/.thumbs/$This")[9];
            }
            else {
               $mtime2 = 0;
            }
            if ($mtime > $mtime2) {
               if ($EXIFThumbnail and $EXIFthumbs and (system("$EXIF -e '$Dir/$This' >/dev/null 2>&1") == 0)) {
                  # Use the EXIF thumbnail
                  if (move ("$Dir/$This.modified.jpeg", "$Dir/.thumbs/$This")) {
                     print "Retrieved EXIF thumbnail for picture: $This\n";
                  } else {
                     die "Could not retrieve EXIF thumbnail for picture: $This\n";
                  }
               } else {
                  # Create our own
                  system "$MAKE_THUMBNAIL \"$Dir/$This\" \"$Dir/.thumbs/$This\"" and die "Could not make thumbnail of $This\n";
                  if (keys %PictureData) {
                     # Picture had EXIF data (but apparently no thumbnail), so remove EXIF data from generated thumbnail
                     CleanJpg("$Dir/.thumbs/$This");
                  }
                  print "Created thumbnail for picture: $This\n";
               }
               chmod (0644, "$Dir/$This");
               chmod (0644, "$Dir/.thumbs/$This");
            }
            my $clean = ($EXIFThumbnail or (keys %PictureData));
            my ($medsizeschanged, $medres, $medsize) = GenerateSmallerImage($Dir, $This, $size, $mtime, $MED_SIZE_CUTOFF, $MAKE_MED, 'med', $sizes, $clean);
            my ($lgsizeschanged, $lgres, $lgsize) = GenerateSmallerImage($Dir, $This, $size, $mtime, $LG_SIZE_CUTOFF, $MAKE_LG, 'large', $sizes, $clean);
            $sizeschanged += ($medsizeschanged + $lgsizeschanged);
            print OUT "   <TR> <TD> <table cellpadding=5 border=0><tr><td>\n";
            if ($medsize > 0) {
               print OUT "<A HREF=.thumbs/med-$This><IMG src=.thumbs/$This></A></TD>\n";
               print OUT "<TD align=center><A HREF=.thumbs/med-$This><I>$medres<BR>$medsize kb</I></A></TD>\n";
            } else {
               print OUT "<A HREF=$This><IMG src=.thumbs/$This></A></TD>\n";
            }
            if ($lgsize > 0) {
               print OUT "<TD align=center><A HREF=.thumbs/large-$This><I>$lgres<BR>$lgsize kb</I></A></TD>\n";
            }
            print OUT "<TD align=center><A HREF=$This><I>$res<BR>$size kb</I></A></TD>\n";
            print OUT "</TD></TR></TABLE><TD> $PicDesc </TD><TD>\n";
            if ($PictureData{'Date and Time'}) {
               my $epoch = FormatEXIFTimeEpoch($PictureData{'Date and Time'});
               if ($Newest < $epoch) {
                  $Newest = $epoch;
               }
               $temp = FormatEXIFTime($PictureData{'Date and Time'});
               print OUT "<B>Taken</B>: $temp</TD><TD>\n";
            } else {
               $temp = FormatTime($mtime);
               print OUT "<B>Posted</B>: $temp</TD><TD>\n";
            }
            if (keys %PictureData) {
               foreach (@EXIFAlways) {
                  if ($PictureData{$_}) {
                     print OUT "<B>$_</B>: $PictureData{$_}<BR>\n";
                     delete $PictureData{$_};
                  }
               }
               print OUT "<div id=\"$This-less\"><button onclick=\"javascript: document.getElementById('$This-more').style.display='inline';document.getElementById('$This-less').style.display='none'\">More Info</button></div>\n";
               print OUT "<div id=\"$This-more\" style=\"display: none\"><button onclick=\"javascript: document.getElementById('$This-less').style.display='inline';document.getElementById('$This-more').style.display='none'\">Hide Info</button></BR>\n";
               foreach (sort keys %PictureData) {
                  print OUT "<B>$_</B>: $PictureData{$_}<BR>\n";
               }
               print OUT "</div>\n";
            }
            print OUT "</TD></TR>\n"; 
            chmod (0644, "$Dir/$This");
         }
      }
      if ($EXIF) {
         print OUT "  <TR><TD COLSPAN=4><I>(Click for larger pictures)</I></TD></TR>";
      } else {
         print OUT "  <TR><TD COLSPAN=3><I>(Click for larger pictures)</I></TD></TR>";
      }
      print OUT "  </TABLE>\n";
      if ($sizeschanged > 0) {
         SaveImageSizes($Dir, $sizes);
      }
   }
   if (-f "$Dir/.footer") {
      open (FOOTER, "$Dir/.footer");
      foreach (<FOOTER>) {
         print OUT $_;
      }
      close (FOOTER);
   }
   if (-f "$Dir/../.name") {
      open (NAME, "$Dir/../.name");
      $PrevName = <NAME>;
      chomp $PrevName;
      close (NAME);
      print OUT "<HR><P>Return to <A HREF=../index.html>$PrevName</A>\n";
   }
   print OUT "<HR><I>This page is maintained by <A HREF=\"mailto:$YourEmail\">$YourName</A>\n";
   print OUT "(Auto-generated by <A href=\"http://linux.kaybee.org/tabs/autoscrapbook/\">AutoScrapbook</A> by <A href=\"mailto:kirk\@kaybee.org\">Kirk Bauer</A>)</I>\n";
   print OUT " </BODY>\n</HTML>\n";
   close (OUT);
   open (OUT, ">$Dir/.newest");
   print OUT $Newest;
   close (OUT);
   open (OUT, ">$Dir/.num");
   print OUT $Num;
   close (OUT);
   rename "$Dir/index.html.new", "$Dir/index.html";
   chmod (0644, "$Dir/index.html");
   chmod (0755, "$Dir");
   print "Created $Dir/index.html\n";
}

sub SaveImageSizes ($$) {
   my ($Dir, $ref) = @_;
   open (SIZES, ">$Dir/.sizes") or die "Could not write to size file: $Dir/.sizes\n";
   foreach (keys %{$ref}) {
      print SIZES "$_=$ref->{$_}\n";
   }
   close (SIZES);
}

sub GetImageSizes ($) {
   my ($Dir) = @_;
   my (%ret);
   if (-f "$Dir/.sizes") {
      my $mtime = (stat "$Dir/.sizes")[9];
      my $line;
      open (SIZES, "$Dir/.sizes") or die "Could not read size file: $Dir/.sizes\n";
      while ($line = <SIZES>) {
         chomp($line);
         my ($file, $size) = split /=/, $line;
         $ret{$file} = $size;
      }
      close (ORDER);
      return (\%ret, $mtime);
   } else {
      return (\%ret, 0);
   }
}

sub SetOrder ($@) {
   my ($Dir, @items) = @_;
   my (@ret, @order);
   my $changed = 0;
   if (-f "$Dir/.order") {
      open (ORDER, "$Dir/.order") or die "Could not open order file: $Dir/.order\n";
      @order = <ORDER>;
      chomp(@order);
      close (ORDER);
   } 
   # Check current entries
   my ($entry, $found, $i, $j);
   for ($i = 0; $i <= $#order; $i++) {
      $entry = $order[$i];
      $found = 0;
      for ($j = 0; $j <= $#items; $j++) {
         if ( ($entry eq $items[$j]) or ($Renamed{$entry} and ($Renamed{$entry} eq $items[$j])) ) {
            splice @items, $j, 1;
            $found = 1;
            last;
         }
      }
      if ($found) {
         # Found a match
         push @ret, $entry;
      } else {
         # File no longer exists... remove from order file
         $changed++;
         splice @order, $i, 1;
         $i--;
      }
   }
   # Anything left are new items not found in order file...
   foreach (@items) {
      push @order, $_;
      push @ret, $_;
      $changed++;
   }
   if ($changed) {
      $Debug and print "Updating order file: $Dir/.order (edit to modify picture order)\n";
      open (ORDER, ">$Dir/.order") or die "Could not write order file: $Dir/.order\n";
      foreach (@order) {
         print ORDER "$_\n";   
      }
      close (ORDER);
   }
   return @ret;
}

sub ProcessDir {
   my $Dir = $_[0];
   my ($Name, $Desc, $changed, $This, $mtime, $mtime2, @items, @contents);
   my $OK = 'y';
   if (-f "$Dir/.name") {
      if ($AskReprocess) {
         print "Re-Process old directory: $Dir? (y/N)\n";
         $OK = GetInput;
      }
   } else {
      print "Process new directory: $Dir? (y/N)\n";
      $OK = GetInput;
   }
   if ($OK !~ /[yY]/) {
      return -1;
   }
   print "Processing directory: $Dir\n";
   chmod (0755, "$Dir");
   unless (-f "$Dir/.name") {
      print "Give a name for directory $Dir:\n";
      $Name = GetInput;
      open (FILE, ">$Dir/.name");
      print FILE "$Name\n";
      close (FILE);
   }
   unless (-f "$Dir/.info") {
      print "Give a description for directory $Dir:\n";
      $Desc = GetInput;
      open (FILE, ">$Dir/.info");
      print FILE "$Desc\n";
      close (FILE);
   }
   opendir (DIR, $Dir);
   @contents = readdir(DIR);
   closedir(DIR);
   $changed = 0;

   # Check to see if main program has changed since the index.html was generated
   if (-f "$Dir/index.html") {
      $mtime = (stat "$Dir/index.html")[9];
   } else {
      $mtime = 0;
      $changed = 1;
   }

   if ($pmtime > $mtime) {
      $changed = 1;
   }

   $Debug and print "$Dir changed(0): $changed\n";

   foreach $This (@contents) {
      unless ($This =~ /^\./) {
         if (-d "$Dir/$This") {
            my $ret = ProcessDir("$Dir/$This");
            $Debug and print "ProcessDir($Dir/$This) returned: $ret\n";
            if ($ret != -1) {
               if ($ret) {
                  $changed = 1;
               }
               push @items, $This . '/';
            }
         } else {
            unless ($This =~ /index\.html/) {
               $mtime2 = (stat "$Dir/$This")[9];
               if ($mtime2 > $mtime) {
                  $changed = 1;
               }
               my $temp = $This;
               $temp =~ s/\.[^\.]+$//;
               if (-f "$Dir/.$temp") {
                  $mtime2 = (stat "$Dir/.$temp")[9];
                  if ($mtime2 > $mtime) {
                     $changed = 1;
                  }
               } elsif (not $JHEAD) {
                  $changed = 1;
               }
               push @items, $This;
            }
         }
      }
   }

   $Debug and print "$Dir changed(1): $changed\n";

   # Check to see if support files were hand-modified since last run
   foreach ('.name', '.info', '.header', '.footer', '.order') {
      if (-f "$Dir/$_") {
         $mtime2 = (stat "$Dir/$_")[9];
         if ($mtime2 > $mtime) {
            $changed = 1;
         }
      }
   }

   $Debug and print "$Dir changed(2): $changed\n";

   # Updade order of items
   @items = SetOrder($Dir, sort @items);

   $Debug and print "Items for $Dir (after sort): @items\n";

   $Debug and print "$Dir changed(3): $changed\n";
   if ($changed) {
      GeneratePage($Dir, @items);
      # Updade order of items
      @items = SetOrder($Dir, sort @items);
      return 1;
   } else {
      return 0;
   }
}

if ($ARGV[0] eq '--nomed') {
   shift;
   $MED_SIZE_CUTOFF=0;
}

if ($ARGV[0] eq '--nolarge') {
   shift;
   $LG_SIZE_CUTOFF=0;
}

unless (@ARGV) {
   push @ARGV, "pics";
}

my $Dir;
foreach $Dir (@ARGV) {
   if (-d $Dir) {
      ProcessDir ($Dir);
   }
}

#####################################################################################
# Copyright (c) 2003 Kirk Bauer
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#####################################################################################

# The following function was taken directly from the ICAT program by Phil Harvey
# The web page for this software is http://www.sno.phy.queensu.ca/~phil/icat/

# rewrite a jpg file, removing EXIF data
# - returns 0 on failure
sub CleanJpg($)
{
  my $infile = shift;
  my $outfile = '';
  my $success = 0;
  my ($a,$b,$s,$length,$dummy);
  my ($ch,$ord_ch);

  open(JPG_IN,$infile) or return($success);
  binmode( JPG_IN );

  # create name of temporary file in same directory
  if ($infile =~ /(.*\/)/) {
    $outfile = $1;
  }
  $outfile .= "icat_CleanJpg.tmp";

  unless (open(JPG_OUT,">$outfile")) {
    close(JPG_IN);
    return($success);
  }

  binmode( JPG_OUT );
  if (read(JPG_IN,$s,2)==2 && $s eq "\xff\xd8") {
    print JPG_OUT $s;
    # read file until we reach an end of image (EOI) or start of scan (SOS)
Marker: for (;;) {
      # Find next marker (JPEG markers begin with 0xff)
      for (;;) {
        read(JPG_IN, $ch, 1) or last Marker;
        print JPG_OUT $ch;
        $ord_ch = ord($ch);
        last if $ord_ch == 0xff;
      }
      # JPEG markers can be padded with unlimited 0xff's
      for (;;) {
        read(JPG_IN, $ch, 1) or last Marker;
        $ord_ch = ord($ch);
        last if $ord_ch != 0xff; # exit loop before printing marker
        print JPG_OUT $ch;
      }
      # Now, $ord_ch is the value of the marker.
      if (($ord_ch >= 0xc0) && ($ord_ch <= 0xc3)) {
        # this is an image block
        read(JPG_IN, $dummy, 7) == 7 or last;
        print JPG_OUT $ch,$dummy;
      # handle stand-alone markers 0x01 and 0xd0-0xd7
      # (and the non-marker 0x00, which follows an 0xff if it exists in data)
      } elsif ($ord_ch==0x00 or $ord_ch==0x01 or ($ord_ch>=0xd0 and $ord_ch<=0xd7)) {
        print JPG_OUT $ch;
      } elsif ($ord_ch==0xd9) { # end of image
        print JPG_OUT $ch;
        $success = 1;
        last;
      } else {
        # We **MUST** skip variables, since FF's within variable names are
        # NOT valid JPEG markers
        read(JPG_IN, $s, 2) == 2 or last;
        ($a, $b) = unpack("C"x2,$s);
        $length = ($a << 8) | $b;   # get length
        last if (!defined($length) || $length < 2);
        read(JPG_IN, $dummy, $length-2) == $length-2 or last;
        # print out this field unless we are ignoring it
        unless ($ord_ch==0xe1) { # ignore EXIF data
          print JPG_OUT $ch,$s,$dummy;
        }
      }
    }
  }
  close(JPG_IN);
  close(JPG_OUT);
  if ($success) {
    unlink($infile);
    rename($outfile,$infile);
    chmod (0644, $infile);
  } else {
    unlink($outfile);
  }
  return($success);
}

