#! /usr/bin/perl
# -*- Mode: Perl -*-
#
# $Id: webmagick.in,v 1.17 1997/04/01 03:12:53 bfriesen Exp $
#
# Create HTML index files and imagemaps corresponding to a
# directory tree of image files. Current name is "WebMagick".
#
# Copyright Bob Friesenhahn (bfriesen@simple.dallas.tx.us) 1996, 1997
#
# This work may be used for any purpose, public or private, provided that
# this work or derivations thereof are attributed to its authors.
#
# NOTE: This program has a manual page.  Considerable time has been
# spent writing it so please read it before asking questions.
#
# Suggestions and moral support were provided by Anthony Thyssen
# (anthony@cit.gu.edu.au). His help is very much appreciated.
#
# Requires the ImageMagick (last tested with 3.8.3) and PerlMagick
# (last tested with 1.0) packages as well as a recent version
# of PERL 5.
#
# Obtain ImageMagick from "ftp://ftp.wizards.dupont.com/pub/ImageMagick"
# or visit the ImageMagick web page at
# "http://www.wizards.dupont.com/cristy/ImageMagick.html".
# Obtain PerlMagick from
# "ftp://ftp.wizards.dupont.com/pub/ImageMagick/perl" or visit the
# PerlMagick web page at
# "http://www.wizards.dupont.com/cristy/www/perl.html".
#
# ImageMagick and PerlMagick are written by John Cristy (cristy@dupont.com).
# WebMagick would not be possible without his wonderful software.  The
# author greatly appreciates Cristy's assistance with ironing out PerlMagick's
# (or WebMagick's bugs) during the development of WebMagick.
#
# Depth-first recursion is supported by the --recurse option except that
# directories represented by symbolic links are ignored.
#
# All generated files use two prefixes and are placed in the same
# directory as the image files. One prefix is for master index files that
# the Web server knows about (e.g. index.html). The other prefix is for
# the remaining files. The dual prefix scheme provides for special
# treatments such as making all page index related files hidden (nicer for
# 'xv').
#
# Selection of GIF vs JPEG montages is made based on file size for the
# client's benefit.
#
# Both client-side imagemaps and server-side (CERN & NCSA) imagemaps are
# supported. For server-side imagemaps to work, a path mapping must be
# done in the server such that paths as reported by /bin/pwd map into the
# equivalent server paths or, the server must support relative imagemap
# paths.
#
$help=('WebMagick: web-based image index builder
Bob Friesenhahn (bfriesen@simple.dallas.tx.us) $Date: 1997/04/01 03:12:53 $

Command line options:
 General:
  --debug	     Print debug messages
  --forcehtml	     Force HTML files to be generated (default off)
  --forcemontage     Force montage (default off)
  --help	     Display usage message
  --recurse	     Recurse directory tree
  --srcdir	     Image directory to process
  --verbose	     Tell us more ...
 
 Paths:
  --absolute         Use fully-qualified URLs
  --iconpath	     Relative path under rootdir to webmagick icons
  --prefixpath       Path to prepend to generated URLs (e.g. /~username)
  --relative	     Translate URLs to relative paths (same filesystem)
  --rootpath	     Absolute path to server root (NCSA DocumentRoot)

 Server-side imagemaps:
  --htimage	     Imagemap CGI program URL (set to \'\' for none)
  --maptype	     Server-side map type ("ncsa" or "cern")
 
 Filenames:
  --dirindexname     Directory-name to title cross-reference file name
  --imgindexname     Image-name to thumbnail label cross-reference file name
  --indexname	     Name of master index files (default server index)
  --pageindexname    Base name of page-related index files
  --readme	     Name of directory info file

 Caching:
  --cache            Cache thumbnails
  --cachedir         Subdirectory name to cache thumbnails in (default .cache)
  --cacheformat      Format of cached thumbnails (default JPEG)
  --cachegeom        Cache thumbnail geometry (default thumbgeom)
  --cachemin         Smallest image to cache in pixels. (default 300*200)

 Montage:
  --forcegif         Force imagemap to be in GIF format
  --maxgif           Maximum size of GIF imagemap before trying JPEG
  --columns	     Montage columns
  --rows	     Montage rows (max)
  --thumbbackground  Montage background color
  --thumbbordercolor Color inside of Frame (unused if no Frame)
  --thumbborderwidth Thumbnail border width (pixels)
  --thumbcompose     Thumbnail image composition operation (default Replace)
                     Over, In, Out, Atop, Xor, Plus, Minus, Add, Subtract,
                     Difference, Bumpmap, Replace, MatteReplace, Mask, Blend,
                     Displace
  --thumbfont	     Thumbnail title font
  --thumbframe       Geometry of frame around thumbnail (default no frame)
  --thumbgeometry    Thumbnail geometry (widthxheight)
  --thumbgravity     Direction thumbnail gravitates to (default Center)
                     NorthWest, North, NorthEast, West, Center, East
                     SouthWest, South, SouthEast. North is up.
  --thumblabel       Format for default thumbnail text label
  --thumbmattecolor  Frame color (if thumbnail frames enabled)
  --thumbshadow      Enable decorative shadow under thumbnail
  --thumbtexture     Texture to tile onto the image background
  --thumbtransparent Transparent color

 HTML Colors & Appearance:
  --address          Optional user address info
  --coloralink	     Link (active) color
  --colorback	     Background color (also applied to JPEG montage background)
  --colorfore	     Foreground text color
  --colorlink	     Link (unvisited) color
  --colorvlink	     Link (visited) color
  --dircoloralink    Link (active) color (directory frame)
  --dircolorback     Background color (directory frame)
  --dircolorfore     Foreground color (directory frame)
  --dircolorlink     Link (unvisited) color (directory frame)
  --dircolorvlink    Link (visited) color (directory frame)

  --header           Page header (imagemap frame)
  --readmevisible    Show README.html on first page rather than just linking.
  --title            Page title
');

###########################################################################
#                     Internal Default Options
###########################################################################
#
#WebMagickRcTop  -- Don't remove this line
#
# Copy the webmagickrc file to your home directory under the name
# .webmagickrc. Any default definitions you want to override should
# be uncommented in this file and modified.
#
# Navigation Icon Paths and URLs
# Specify the path and file name for the navigation icons.
# !!!MUST EDIT OR OVERRIDE!!!
#
$opt_rootpath	= '/home/httpd/html';	# Directory Path to top of html tree
				# Needed to determine relative paths to images
$opt_prefixpath	= ''; # Path or URL to prepend to root URL
				# Not used if local relative paths used
$opt_iconpath	= 'Images/webmagick';
				# Relative path under rootpath / prefixpath

#
# Server-side imagemap settings
# !!!MAY NEED TO EDIT OR OVERRIDE!!!
# 
$opt_htimage='';	# Base URL to server-side imagemap CGI
				#  On some systems this is  /cgi-bin/imagemap
				#  Set to '' to use a ".map" URL with relative
                                # URLs (latest NCSA & Apache)
$opt_maptype='ncsa';	# Maptype must be "cern" or "ncsa". If you are
				# using Apache, specify "ncsa".

# RC files
$global_option_file	= "$ENV{'HOME'}/.webmagickrc"; # global rc file
$webmagickrc		= '.webmagickrc';# Name of per-directory rc file

# File naming
$opt_indexname		= 'index.html';	# Per-directory master index file 
$opt_readme		= 'README.html';# Name of welcome page README file ('' = none)
$opt_pageindexname	= '.index';	# Base name of secondary index files
$opt_dirindexname	= '.dirindex';	# Subdirectory Title cross-reference
					#  dirname   Directory Title
$opt_imgindexname	= '.imgindex';	# Image name to label cross-reference file

#
# HTML Color related options
#
# X11 RBG color database location
$opt_rgbdb='/usr/X11/lib/X11/rgb.txt';
#
# Page Frame & non-framed pages
$opt_colorback		= 'peach puff';	# Color -- Background
$opt_colorfore		= 'black';	# Color -- Foreground

$opt_coloralink		= '#FF0000';	# Color -- Active link
$opt_colorlink		= '#0000EE';	# Color -- Link
$opt_colorvlink		= '#551A8B';	# Color -- Visited link
#
# Directory frame (Leave options empty ('') to use page frame colors
$opt_dircolorback	= 'light sky blue'; # Color -- Background
$opt_dircolorfore	= 'black';	# Color -- Foreground
$opt_dircoloralink	= '';		# Color -- Active link
$opt_dircolorlink	= '';		# Color -- Link
$opt_dircolorvlink	= '';		# Color -- Visited link


# General options
$opt_absolute		= 0;	# Use abolute URLs (default off) see $opt_relative
$opt_debug		= 0;	# Debug flag (default off)
$opt_recurse		= 0;	# Recursivally apply webmagick (default off)
$opt_prune		= 0;	# Do Not recurse into subdirectories (off)
$opt_ignore		= 0;	# Do not webmagick this directory
				#    but still recurse into sub-directories
$opt_relative		= 1;	# Relative paths flag (default on)
$opt_srcdir		= '.';	# Source directory path (current directory)
$opt_verbose		= 0;	# Verbose flag (default off)
$opt_forcehtml		= 0;	# Force HTML files to be generated (default off)
$opt_forcemontage	= 0;	# Force montage (default off)
$opt_forcegif		= 0;	# Force GIF imagemaps (default off)
$opt_help		= 0;	# Display usage message
$opt_header		= '';	# Imagemap extra page header (HTML)
$opt_readmevisible	= 0;	# Make README.html be first page.
$opt_title		= '';	# Page title (blank provides default title)
$opt_address		= '';	# Additonal address info for bottom of
                                #    imagemap page

#				
# ImageMagick Montage settings
#
$opt_maxgif		= 30000;	# Maximum GIF imagemap size before
					#  trying JPEG
$opt_columns		= 6;		# Max number of columns in montage grid
$opt_rows		= 4;		# Max number of rows in montage grid
$opt_thumbtexture	= 'false';	# Texture to tile onto the image background
$opt_thumbbackground	= 'peach puff';	# Montage background color
$opt_thumbborderwidth	= 0;		# Thumbnail border width (pixels)
$opt_thumbbordercolor	= 'black';	# Inside of Frame color.  Unused if no frame
$opt_thumbmattecolor	= '#CCCCCC';	# Color -- Frame Color (#C0C0C0?)
$opt_thumbtransparent	= 'peach puff';	# Color -- Image Transparency
$opt_thumbcompose	= 'Replace';	# Thumbnail image composition operation
$opt_thumbfont		= '6x13';	# Font used for thumbnails
$opt_thumbframe		= 'false';	# Geometry of frame around thumbnail
					#    (default false)
$opt_thumbgeometry	= '106x80+2+2>'; # Size of thumbnail images (width x height)
$opt_thumbgravity	= 'Center';	# Direction thumbnail gravitates to
					#    (default Center)
$opt_thumblabel		= '%f\n%wx%h %b'; # Default format for thumbnail text label
$opt_thumbshadow	= 'false';	# Enable decorative shadow under thumbnail
					#    (default disabled)

#
# Caching related options
#
# Note: See below about sharing cache with 'xv'
#
# If caching is turned on, the montage grid will continue to be controlled
# by opt_thumbgeometry but the thumbnail size will be controlled by opt_cachegeom
#
$opt_cache		= 1;		# Cache thumbnails
$opt_cachedir		= '.cache';	# Subdirectory to cache thumbnails in
$opt_cacheformat	= 'JPEG';	# Format to use for thumbnails
$opt_cachegeom		= $opt_thumbgeometry; # Thumbnail geometry
$opt_cachemin		= 300*200;	# Smallest cached image in total pixels
					#  (width * height).  Images smaller than
					#  this size will not be cached.

# Uncomment these options to share cache with John Bradley's 'xv' (Visual Schnauzer format)
#$opt_cacheformat	= 'P7';		# 'xv's thumbnail format
#$opt_cachegeom		= '80x60';	# 'xv's default thumbnail geometry is 80x60
#$opt_cachedir		= '.xvpics';	# 'xv's thumbnail cache directory

#
#WebMagickRcBottom -- Don't remove this line
#

# Hash table of icons used -- image size read internally by webmagick
%opt_icons = (
   'prev',	'blue_prev.gif',	# Previous
   'next',	'blue_next.gif',	# Next
#  'prev_grey',	'gray_prev.gif',	# Previous (grayed out) NOT USED (YET)
   'next_gray',	'gray_next.gif',	# Next (grayed out)
   'up',	'blue_up.gif',  	# Up
   'help',	'blue_readme.gif',	# Help Readme File
#  'help',	'blue_help.gif',	# Help Alternative (Question)
#  'dir',	'blue_dir.gif', 	# Directory List Icon (See below)
   'ball',	'blue_ball.gif',	# A ball matching other icons
);

#
# Format Templates
# WARNING: This is for expert web and perl programmers only
# do not modify unless you know what you are doing.
# 
# Extra Images can be added to the above hash table and then used
# in the following format options. For example the 'dir' icon above
# can be uncommented then the following lines added below.
# WARNING: this is only useful if $opt_indexname is something else.
# 
# <A HREF=\"./\" TARGET=\"pagemap\">
#    <IMG SRC=\"$icon_url{dir}.gif\" $icon_size{dir} ALT=\"\" BORDER=0></A>
#    <A HREF=\"./\" TARGET=\"pagemap\">Dir Listing</A><BR>
#

#
# Template for the Frame definition
# This allows adding frames, changing geometry, etc.
#
$opt_framefmt='
<FRAMESET COLS=\"110,*\" FRAMEBORDER=NO BORDER=1>
 <FRAMESET ROWS=\"*\">
   <FRAME SRC=\"${dirframelink}\" NAME=\"directories\" MARGINWIDTH=3>
   <FRAME SRC=\"${pageframelink}\" NAME=\"pagemap\" MARGINWIDTH=3>
 </FRAMESET>
</FRAMESET>
';


#
# Template for the Directory Index Frame
#
$opt_frameddirfmt='
<P>
<FONT SIZE=-1>
${uphtml}
${nexthtml}
</P>
${dirhtml}
';

#
# Template for Non-Framed Top Index Page ($opt_indexname)
#
$opt_dirfmt='\n<H3>Directory Navigator ...</H3>
${uphtml}
${helphtml}
${dirhtml}
${pageindexhtml}
';

#
# File extensions that we support
#
@extensions=( 'avs', 'bmp', 'cgm', 'eps', 'gif', 'hdf',
	     'jbig', 'jpeg', 'jpg', 'mif', 'miff', 'mpeg', 'mpg',
	     'pcl', 'pcx', 'pdf', 'pic', 'png', 'png', 'pnm', 'ppm',
	     'ps', 'rle', 'tga', 'tif', 'tiff', 'xbm', 'xpm', 'xwd');

###########################################################################
#                  End of Internal Default Options
###########################################################################

select(STDERR); $| = 1;		# Make stderr unbuffered
select(STDOUT); $| = 1;		# Make stdout unbuffered

umask( 022 );			# Sets default file mode 644
$start_time = time;		# Save start time

#
# Allow global options file to override defaults set above
# (but not command line options)
#
&source_rc( $global_option_file )
    || die("Error sourcing $global_option_file\n");

#
# Eval per-directory rc files if they exist.
# Rc files are evaluated for each directory starting from
# $opt_rootdir until the current directory is reached. This supports
# "additive" behavior for a branch in the tree.
#
&eval_rc();

# Set signal handler to gracefully abort (hah!)
# Handle signal induced exits properly
sub sig_handler {
    local($sig) = @_;
    print("\nCaught signal SIG$sig -- aborting ...\n");
    exit(1);
}
$SIG{'HUP'} = 'sig_handler';
$SIG{'INT'} = 'sig_handler';
$SIG{'QUIT'} = 'sig_handler';

#
# Get version info from RCS variables
#
{
    local($crap);
    ($crap, $webmagick_revision ) = split( ' ', '$Revision: 1.17 $' );
    ($crap, $webmagick_date ) = split( ' ', '$Date: 1997/04/01 03:12:53 $' );
}

#
# We don't really like command line options but we'll support them anyway. :-)
#
require('newgetopt.pl');
if ( ! &NGetOpt(
		'absolute',
		'address:s',
		'cache',
		'cachedir:s',
		'cacheformat:s',
		'cachegeom:s',
		'cachemin:i',
		'coloralink:s',
		'colorback:s',
		'colorfore:s',
		'colorlink:s',
		'colorvlink:s',
		'columns:i',
		'debug',
		'dircoloralink:s',
		'dircolorback:s',
		'dircolorfore:s',
		'dircolorlink:s',
		'dircolorvlink:s',
		'dirindexname:s',
		'forcegif',
		'forcehtml',
		'forcemontage',
		'header:s',
		'help',
		'htimage:s',
		'iconpath:s',
		'imgindexname:s',
		'indexname:s',
		'maptype:s',
		'maxgif:i',
		'pageindexname:s',
		'prefixpath:s',
		'readme:s',
		'readmevisible',
		'recurse',
		'relative',
		'rootpath:s',
		'rows:i',
		'srcdir:s',
		'thumbtexture:s',
		'thumbbackground:s',
		'thumbborderwidth:i',
		'thumbbordercolor:s',
		'thumbcompose:s',
		'thumbfont:s',
		'thumbframe:s',
		'thumbgeometry:s',
		'thumbgravity:s',
		'thumblabel:s',
		'thumbshadow:s',
		'thumbtransparent:s',
		'title:s',
		'verbose' 
		)
    ) {
    &help;
    exit(0);
}

#
# Print help message
#
if( $opt_help ) {
    &help;
    exit(0);
}

#
# Check if source directory is valid
#
if ( ! -d "${opt_srcdir}" ) {
    print( STDERR "No ${opt_srcdir} directory\n" );
    exit(1);
}


#
# Open X11 RGB database
#
if ( -f $opt_rgbdb ) {
    open( RGBDB, "<$opt_rgbdb" )
	|| die("Unable to open RGB database $opt_rgbdb");
    while( <RGBDB> ) {
        local($red, $green, $blue, $color);
        chop;
        s/^[ \t]+//; # Remove any preceding spaces
        ($red, $green, $blue, $color) = split( /[ \t]+/, $_, 4);
        $RGBDB{"\L$color"} = sprintf("#%02X%02X%02X", $red, $green, $blue);
    }
    close( RGBDB );
} else {
    print( STDERR "Warning: RGB database \'$opt_rgbdb\' not found\n" );
}

#
# Build-up regular expression for file extensions we accept.
#
$include='';
foreach $ext (@extensions) {
    ($uext = $ext) =~ tr/[a-z]/[A-Z]/;
    if($include) {
	$include .= "|";
    }
    $include .= "(\\.${ext}\$)|(\\.${uext}\$)";
}

#
# Build-up regular expression for file names we don't accept
#
$exclude="(^$opt_indexname)|(\\.html\$)|(^\\.)";

#
# Eval per-directory rc files if they exist.
# Rc files are evaluated for each directory starting from
# $opt_rootdir until the current directory is reached. This supports
# "additive" behavior for a branch in the tree.
#
&eval_rc();

#
# Translate paths to physical paths (avoid symlink problems)
#
$opt_srcdir	= &lets_get_physical( $opt_srcdir );
$opt_rootpath 	= &lets_get_physical( $opt_rootpath );
$icon_dir_path  = "${opt_rootpath}/${opt_iconpath}";


#
# html_imgsize
# Obtain image size and return HTML text (HEIGHT=foo WIDTH=bar)
# representing size This can be slow since PerlMagick reads the entire
# image.
#
sub html_imgsize {
    local($file) = @_;
    local($retval);

    use Image::Magick;

    my ( 
	$image,
	$imgheight,
	$imgwidth,
	$status
	);

    $retval = '';
    if( -f $file ) {
        $image = Image::Magick->new;
        $status = $image->Read($file);
        if( "$status" ) {
	    undef $image;
	    return( $retval );
	}
        ($imgwidth, $imgheight) = $image->[0]->Get("columns", "rows");
        print( STDERR "Image \"$file\" has dimensions ",
	      "${imgwidth}x${imgheight}\n") if $opt_debug;
	$retval="HEIGHT=${imgheight} WIDTH=${imgwidth}";
	undef $image;	# Free up space
    } else {
        print( STDERR "html_imgsize: no such file \"$file\"\n" );
    }
    return( $retval );
}

#
# Use Randy Ray's (rjray@uswest.com) Image::Size module (v2.2)
# to obtain in-line image sizes. Obtained from CPAN. Requires
# IO module (1.15) which is available from CPAN or included
# in post 5.003 betas of PERL.
# If you obtain and install this module, then comment out the
# html_imgsize subroutine above and uncomment the 'use' line.
#
#use Image::Size 'html_imgsize';

#
# Get icon image sizes
#
for $icon ( keys %opt_icons ) {
    my $icon_path = $icon_dir_path .'/'.  $opt_icons{$icon};
    if( !defined( $icon_paths{$icon} ) || ( $icon_paths{$icon} ne $icon_path )) {
	$icon_paths{$icon} = $icon_path;
	$icon_size{$icon} = &html_imgsize( $icon_path );
    }
}


if( $opt_recurse ) {
    # Recurse depth-first under current directory, executing &wanted
    # for each directory ignoring hidden directories
    require "find.pl";
    print( "Processing directory tree $opt_srcdir ...\n" ) if $opt_debug;
    &find("$opt_srcdir");
} else {
    print( "Processing directory $opt_srcdir ...\n" ) if $opt_debug;
    &dodir("$opt_srcdir");
}

#
# Print run times if running in verbose mode
#
if( $opt_verbose ) {
    local(
	  $user,          # CPU time in user code for this process
	  $system,        # CPU time in system code on behalf of this process
	  $cuser,         # CPU time in user code of child processes
	  $csystem,       # CPU time in system code on behalf of child processes
	  $total_user,    # Total user time (parent + children)
	  $total_system,  # Total system time (parent + children)
	  $total_time     # Total elapsed time (wall clock)
	  );
    local(
	  $user_m,        # User time in minutes
	  $system_m,      # System time in minutes
	  $cuser_m,       # Child process user time in minutes
	  $csystem_m,     # Child process system time in minutes
	  $total_user_m,  # Total user time in minutes
	  $total_system_m,# Total system time in minutes
	  $total_time_m   # Total elapsed time (wall clock) in minutes
	  );
    ($user, $system, $cuser, $csystem) = times;
    $user_m		= &elapsedminutes( $user );
    $system_m		= &elapsedminutes( $system );
    $cuser_m		= &elapsedminutes( $cuser );
    $csystem_m		= &elapsedminutes( $csystem );
    $total_user		= $user + $cuser;		# Total user time
    $total_user_m	= &elapsedminutes( $total_user );
    $total_system	= $system + $csystem;		# Total system time
    $total_system_m	= &elapsedminutes( $total_system );
    $total_time		= time - $start_time;		# Total run time
    $total_time_m	= &elapsedminutes( $total_time );
    print( STDERR "Run time statistics:\n" );
    print( STDERR "Detailed times: ${user_m} user, ${system_m} system,",
	  " ${cuser_m} child_user, ${csystem_m} child_system\n" );
    print( STDERR "Summary times : ${total_time_m} total,",
	  " ${total_user_m} user, ${total_system_m} system\n" );
}
print( STDERR "Doing normal exit with code 0\n" ) if $opt_debug; 
exit(0);

#####################
#####################

# Executed for each find operation 
# Want:
# is directory
# not hidden directory
sub wanted {
    local($dev,$ino,$mode,$nlink,$uid,$gid,$saved_opt_recurse);
    ($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_);
    if ( -d $_ && !/^\..+/ ) {
        if( $_ ne '.' && &get_rc_var('.', 'opt_prune', 0) ) {
            $prune=1;
            print( STDERR "Pruning    $name\n" );
            return;
        } 
        &forking_dodir($name);
    }
}

#
# Execute dodir with the protection of a fork
# This ensures that current directory and global
# webmagick configuration values are preserved between
# directories.
#
sub forking_dodir {
    local($srcdir) = @_;        # Directory to process
    local($waitpid);            # PID returned by wait
    local($childstat);          # Status returned from child
FORK:
    {
	if( $pid = fork ) {
	    # parent here
	    # child process pid is available in $pid
	    $waitpid=wait();
	    $childstat=$?;
	    # If clean exit, then return 0
	    return 0 if ( $childstat == 0 );
	    syserror( $childstat ); # Print status from error
	    return 1;
	} elsif ( defined $pid ) {  # $pid is zero here if defined
	    # child here
	    # parent process pid is available with getppid
	    &dodir( $srcdir );
	    exit( 0 );
	} elsif ( $! =~ /No more process/ ) {
	    # EAGAIN, supposedly recoverable fork error
	    sleep 5;
	    redo FORK;
	} else {
	    # weird fork error
	    die( "Can't fork: $!\n$@\n" );
	}
    }
    return(0);			# Should never get here!
}

#
# Generate index files for directory specified by $srcdir
#
sub dodir {
    local($srcdir) = @_;	# Directory to process
    local(@allimgfiles);	# List of all source file names in directory
    local(@srcfiles);		# List of source file names for current page
    local(@subdirectories);	# List of directories under this directory
    
    local($maxfiles)=0;		# Maximum number of index links per page
    local($numpages)=0;		# Number of index pages to be generated
    local($numimages)=0;	# Number of images in directory
    local($numdirectories)=0;	# Number of subdirectories in directory
    local($pagenum)=0;		# Current index page number (1 to N)

    #
    # Change current directory to $srcdir
    #
    chdir($srcdir)
	|| die("Can't cd to $srcdir\n$@\n");
    
    # Get current (absolute) directory
    $currentdir=&cwd;
    
    #
    # Eval per-directory rc files if they exist.
    # Rc files are evaluated for each directory starting from
    # $opt_rootdir until the current directory is reached. This supports
    # "additive" behavior for a branch in the tree.
    #
    &eval_rc();

    #
    # Decide if we want to process this directory or not based
    # on the value of $opt_ignore.  If not, then just return.
    #
    if( $opt_ignore ) {
        # Skip this directory
        print( STDERR "Skipping   $srcdir\n" );
        return( 0 );
    } else {
        print( STDERR "Processing $srcdir\n" );
    }

    #
    # Translate paths to physical paths (avoid symlink problems)
    # Note: This duplicates code executed during initialization
    #
    $opt_srcdir		= &lets_get_physical( $opt_srcdir );
    $opt_rootpath 	= &lets_get_physical( $opt_rootpath );
    $icon_dir_path	= "${opt_rootpath}/${opt_iconpath}";

    #
    # Get icon image sizes
    # Note: This duplicates code executed during initialization
    #
    for $icon ( keys %opt_icons ) {
	my $icon_path = $icon_dir_path .'/'.  $opt_icons{$icon};
	if( !defined( $icon_paths{$icon} ) || ( $icon_paths{$icon} ne $icon_path )) {
	    print( STDERR "New icon size for path $icon_path\n" ) if $opt_debug;
	    $icon_paths{$icon} = $icon_path;
	    $icon_size{$icon} = &html_imgsize( $icon_path );
	}
    }

    #
    # Set time and date related variables for general use
    #
    @calendar_months=( 'January', 'February', 'March', 'April', 'May', 'June',
		      'July', 'August', 'September', 'October', 'November',
		      'December');
    ($td_seconds, $td_minutes, $td_hours, $td_mday, $td_month, $td_year,
     $td_wday, $td_yday, $td_isdst ) = localtime(time);

    #
    # Default directory frame colors to page colors if not set
    #
    $opt_dircolorback 	= $opt_colorback  if ! $opt_dircolorback;
    $opt_dircolorfore 	= $opt_colorfore  if ! $opt_dircolorfore;
    $opt_dircoloralink	= $opt_coloralink if ! $opt_dircoloralink;
    $opt_dircolorlink 	= $opt_colorlink  if ! $opt_dircolorlink;
    $opt_dircolorvlink	= $opt_colorvlink if ! $opt_dircolorvlink;

    #
    # Convert all HTML colors to hex format
    #
    $opt_colorback	= &lookuprgb( $opt_colorback );
    $opt_colorfore	= &lookuprgb( $opt_colorfore );
    $opt_coloralink	= &lookuprgb( $opt_coloralink );
    $opt_colorlink	= &lookuprgb( $opt_colorlink );
    $opt_colorvlink	= &lookuprgb( $opt_colorvlink );
    $opt_dircolorback	= &lookuprgb( $opt_dircolorback );
    $opt_dircolorfore	= &lookuprgb( $opt_dircolorfore );
    $opt_dircoloralink	= &lookuprgb( $opt_dircoloralink );
    $opt_dircolorlink	= &lookuprgb( $opt_dircolorlink );
    $opt_dircolorvlink	= &lookuprgb( $opt_dircolorvlink );

    #
    # Calculate the maximum number of images per index page
    # 
    $maxfiles=$opt_columns*$opt_rows;

    #
    # If $opt_absolute is true then $opt_relative (the default)
    # should be set false
    #
    $opt_relative = 0 if $opt_absolute;

    #
    # Now put the PerlMagick Montage options together
    #
    $montageargs =  "\n ";

    $montageargs .= "background=>\'$opt_thumbbackground\',\n "
	if $opt_thumbbackground ne 'false';

    $montageargs .= "borderwidth=>$opt_thumbborderwidth,\n ";

    $montageargs .= "compose=>\'$opt_thumbcompose\',\n "
	if $opt_thumbcompose ne 'false';

    $montageargs .= "font=>\'$opt_thumbfont\',\n "
	if $opt_thumbfont ne 'false';

    $montageargs .= "frame=>\'$opt_thumbframe\',\n "
	if $opt_thumbframe ne 'false';

    $montageargs .= "geometry=>\'${opt_thumbgeometry}\',\n "
	if $opt_thumbgeometry ne 'false';

    $montageargs .= "gravity=>\'$opt_thumbgravity\',\n "
	if $opt_thumbgravity ne 'false';

    $montageargs .= "shadow=>\'$opt_thumbshadow\',\n "
	if $opt_thumbshadow ne 'false';

    $montageargs .= "texture=>\'$opt_thumbtexture\',\n "
	if $opt_thumbtexture ne 'false';

    $montageargs .= "tile=>\'${opt_columns}x${opt_rows}\',\n ";

    $montageargs .= "transparent=>\'$opt_thumbtransparent\'\n "
	if $opt_thumbtransparent ne 'false';

    #
    # Compute a conglomeration of all parameters that
    # effect the montage to use for comparison with the
    # parameters used in the last run.
    #
    $montageparams = "$montageargs";
    $montageparams .= "bordercolor=>\'$opt_thumbbordercolor\',\n "
	if $opt_thumbbordercolor ne 'false';
    $montageparams .= "mattecolor=>\'$opt_thumbmattecolor\'\n "
	if $opt_thumbmattecolor ne 'false';
    $montageparams .= "label=>\'$opt_thumblabel\'"
	if $opt_thumblabel ne 'false';

    #
    # Compute a conglomeration of all parameters that
    # effect the HTML to use for comparison with the
    # parameters used in the last run.
    #
    $htmlparams  = "address=>\'$opt_address\',\n ";
    $htmlparams .= "coloralink=>\'$opt_coloralink\',\n ";
    $htmlparams .= "colorback=>\'$$opt_colorback\',\n ";
    $htmlparams .= "colorfore=>\'$opt_colorfore\',\n ";
    $htmlparams .= "colorlink=>\'$opt_colorlink\',\n ";
    $htmlparams .= "colorvlink=>\'$opt_colorvlink\',\n ";
    $htmlparams .= "dircoloralink=>\'$opt_dircoloralink\',\n ";
    $htmlparams .= "dircolorback=>\'$opt_dircolorback\',\n ";
    $htmlparams .= "dircolorfore=>\'$opt_dircolorfore\',\n ";
    $htmlparams .= "dircolorlink=>\'$opt_dircolorlink\',\n ";
    $htmlparams .= "dircolorvlink=>\'$opt_dircolorvlink\',\n ";
    $htmlparams .= "dirindexname=>\'$opt_dirindexname\',\n ";
    $htmlparams .= "header=>\'$opt_header\',\n ";
    $htmlparams .= "imgindexname=>\'$opt_imgindexname\',\n ";
    $htmlparams .= "indexname=>\'$opt_indexname\',\n ";
    $htmlparams .= "pageindexname=>\'$opt_pageindexname\',\n ";
    $htmlparams .= "readme=>\'$opt_readme\',\n ";
    $htmlparams .= "readmevisible=>\'$opt_readmevisible\',\n ";
    $htmlparams .= "relative=>\'$opt_relative\',\n ";
    $htmlparams .= "title=>\'$opt_title\'";


    #
    # Ensure that maptype is lower case
    # 
    $opt_maptype = "\L${opt_maptype}";

    #
    # Compute icon URLs
    # Make paths relative if in current filesystem and -relative specified
    #
    if ( $opt_relative ) {
    	# Convert to relative URL
        $icon_base_url = &abs_path_to_rel($icon_dir_path);
    } else {
        # Convert to absolute URL
        $icon_base_url = &escapeurl( &abs_path_to_url($icon_dir_path));
    }

    print( "Icon URLs:\n" )  if $opt_debug;
    for $icon ( keys %opt_icons ) {
	$icon_url{$icon}  =  "$icon_base_url" . '/' . $opt_icons{$icon};
	printf( " \$icon_url%-14s = \"%s\"\n", "{'$icon'}", $icon_url{$icon} )
	    if $opt_debug;
    }

    #
    #  Read source file names (if any)
    #  Filter out any names matching the exclude list
    #
    opendir( SRCDIR, ".")
        || die("$0: Failed to open directory $srcdir\n$@\n");
    @allfiles = sort(grep(!/$exclude/,readdir( SRCDIR )));
    closedir( SRCDIR );

    #
    # Build list of image files
    #
    @allimgfiles = grep( /$include/, @allfiles);

    #
    # Find subdirectory names (if any) ignoring hidden directories
    # and directories without index files. Directories should have
    # index files since our find goes from the bottom up and we
    # should have already processed the subdirectories.
    #
    # Only test files that are not in the allimgfiles list
    { 
        local(%tarray);
	local(@dirfiles);
        grep($tarray{$_}++, @allimgfiles);
        @dirfiles = grep(! $tarray{$_},@allfiles);
 
	foreach $_ (@dirfiles) {
	    #if( -f "${_}/${opt_indexname}" ) { # If directory & index file
                                                #  exists then add to list
	    if( -d "${_}" ) {			# If directory exists
		push(@subdirectories, $_); 	#  then add it to the list
	    }
	}
    }

    #
    # Determine the number of index pages to be generated, etc.
    #
    $numimages=scalar(@allimgfiles);		# Number of images
    $numdirectories=scalar(@subdirectories); 	# Number of subdirectories
    $numpages=int($numimages/$maxfiles);
    if ( $numimages%$maxfiles != 0 ) {
        ++$numpages;
    }

    #
    # Check for README file and set havereadme flag if exists
    #
    $havereadme = 0;
    if( -f "${opt_readme}" ) {
    	$havereadme = 1;
    }

    # Set haveimages flag if there are images in directory. This
    # effects the way the directory listing appears.
    $haveimages = 0;
    if( $numimages > 0 ) {
    	$haveimages = 1;
    }

    #
    # Handle a directory name to title index file
    # Store alternative names in %dirnames
    #
    undef( %dirnames );
    if ( -f $opt_dirindexname ) {
        open( DIRINDEX, "<$opt_dirindexname" );
	while( <DIRINDEX> ) {
	    chop;
	    ( $dirname, $dirtitle) = split( /[ \t]+/, $_, 2);
	    $dirnames{$dirname} = &escapehtml($dirtitle);
	    #print(STDERR "dirname=$dirname  dirtitle=$dirtitle\n ");
	}
	close( DIRINDEX );
    }


    #
    # Handle a image name to thumbnail label index file
    # Store alternative names in %imgnames
    #
    undef( %imgnames );
    if ( -f $opt_imgindexname ) {
        open( IMGINDEX, "<$opt_imgindexname" );
	while( <IMGINDEX> ) {
	    chop;
	    ( $imgname, $imgtitle) = split( /[ \t]+/, $_, 2);
	    $imgnames{$imgname} = $imgtitle;
	    #print(STDERR "imgname=$imgname  imgtitle=$imgtitle\n ");
	}
	close( IMGINDEX );
    }
    
    #
    # Determine page title
    #
    if( $opt_title ne '' ) {
        $title = $opt_title;
    } else {
        $dirname=&basename($srcdir);
        $title = "Index of directory \"$dirname\"";
    }

    #
    # Print statistics message
    #
    print( STDERR "   $numimages Images $numdirectories Directories",
	  " $numpages Pages --- " )
	if $opt_verbose;

    #
    # Loop through file list, building pages for each $maxfiles images
    # Do at least one page (there might not be any images)
    #
    @imgfiles = @allimgfiles; # Save value for later
    $pagenum=1;
    while (scalar(@allimgfiles) > 0 || $pagenum == 1) {
        
        print(STDERR " $pagenum" ) if $opt_verbose;

        @srcfiles=splice(@allimgfiles,0,$maxfiles);
        $numfiles=scalar(@srcfiles);

        #
        # Calculate per-page file names based on $pagenum
        #
        &setpagefnames;

	#
	# Decide if we need to do HTML & montage
        #
        $domontage=0;	# Do montage
        $doindexhtml=0;	# Do master index page
        $dopagehtml=0;	# Do page HTML

        #
        # Use status file from last run if available
        #
        if ( -f $pagestat && &source_rc($pagestat)) {

            # Obtain last modified date for status file
            $pagestattime=&fmtime($pagestat);

	    # If file list is different than last time,
            # then do page html & montage
            if( "$stat_srcfiles" ne join(' ',@srcfiles) ) {
                print( STDERR "Need to do both montage and page HTML because",
		      " file list differs\n") if $opt_debug;
                ++$domontage;
                ++$dopagehtml;
            }

            # If directory list is differenet than last time,
            # then do index html
            if( "$stat_subdirectories" ne join(' ',@subdirectories) ) {
                print( STDERR "Need to do index HTML because directory list",
		      " has changed\n")
		    if $opt_debug;
                ++$doindexhtml;
            }

            # If directory index exists and is newer than status file, then
            # regen index page
            if( -f $opt_dirindexname &&
	       (&fmtime($opt_dirindexname) > $pagestattime) ) {
                print( STDERR "Need to do index HTML because directory xref",
		      " has changed\n") if $opt_debug;
                ++$doindexhtml;
            }

            # If number of pages is different than last time, then
            # do index html
            if( $stat_numpages != $numpages ) {
                print( STDERR "Need to do index HTML because number of pages",
		      " has changed\n") if $opt_debug;
                ++$doindexhtml;
            }

            # If montage options differ from last time, then do montage
            if( $montageparams ne $stat_montageopts ) {
                print( STDERR "Need to do montage because options have ",
		      "changed\n") if $opt_debug;
                ++$domontage;
            }

            # If HTML options differ from last time, then re-do HTML
            if( $htmlparams ne $stat_htmlparams ) {
                print( STDERR "Need to re-do HTML because options have ",
		      "changed\n") if $opt_debug;
                ++$dopagehtml;
		++$doindexhtml;
            }

	} else {

            # Status file didn't exist or it failed to parse
            print( STDERR "Skipping status checks due to missing or ",
		  "defective $pagestat file\n" ) if $opt_debug;
            ++$doindexhtml;	# This forces write of status file
        }
	# Montage specific checks
	# Check for missing output files
	# Check for new input files
        if( $numfiles > 0 ) {
            if( ! -f $pagestat || ( ! -f $montagegif && ! -f $montagejpeg )
	       || ! -f $montageshtml ) {
                # If key file is missing then do montage
                print(STDERR "\nMust do montage because a required output",
		      " file is missing\n")
		    if $opt_debug;
                ++$domontage;
            } else {
                # If any file in file list is newer than status file,
                # then do montage
                foreach $file (@srcfiles) {
                    if( &fmtime($file) > $pagestattime ) {
                        print( STDERR "Need to do both montage and HTML",
			      " because file has been updated\n")
			    if $opt_debug;
                        ++$domontage;
                        ++$dopagehtml;
                        print(STDERR "\nMust do montage and html: file",
			      " updated\n") if $opt_debug;
                    }
                }
            }
        }

	# HTML specific checks
	# Check for missing files
	if( ! -f $pagestat || ! -f $htmlindex ) {
				# If key file is missing then do HTML
            print(STDERR "\n   Must do page and index HTML: output file",
		  " missing\n") if $opt_debug;
            ++$dopagehtml;
            ++$doindexhtml;
	}
        # If README file has appeared or vanished, then do HTML
        if( $stat_havereadme != $havereadme ) {
            print(STDERR "\n   Must do HTML: README status changed\n")
                if $opt_debug;
            ++$dopagehtml;
        }

	# Overrides
	if( $opt_forcehtml ) {
	    ++$dopagehtml;
            ++$doindexhtml;
	}
	if( $opt_forcemontage ) {
	    ++$dopagehtml;		# Montage effects HTML output
	    ++$domontage;
	}

PAGES:	{
	    $errorstat = 1;	# Cleared for non-error block exit

	    #
	    # Build montage for current page
	    #
	    if( $domontage && ( $numfiles > 0 ) ) {
		&domontage(@srcfiles) && last PAGES;
	    }
	
	    #
	    # Write out page index file for current page
	    #
	    if( $dopagehtml ) {
		&writeindexfile(@srcfiles);
	    }

	    # Write client-side imagemap file
	    if( $dopagehtml && ( $numfiles > 0 ) ) {
		&writeimagemap && last PAGES;
	    }

	    # Save status (source files and montage options)
            if ( $dopagehtml || $doindexhtml || $domontage ) {
	        open( STAT, ">$pagestat" )
		    || die( "Unable to open file $pagestat!\n$@\n" );
	        print( STAT "\$stat_srcfiles=\'" . join( ' ', @srcfiles)
		      . "\'" . "\;\n" );
                print( STAT "\$stat_subdirectories=\'"
		      . join( ' ', @subdirectories) . "\'" . "\;\n" );
                ($stat_montageopts=$montageparams) =~ s/\'/\\'/g;
	        print( STAT "\$stat_montageopts=\'$stat_montageopts\'\;\n" );
                ($stat_htmlparams=$htmlparams) =~ s/\'/\\'/g;
	        print( STAT "\$stat_htmlparams='$stat_htmlparams\'\;\n" );
                print( STAT "\$stat_havereadme=$havereadme\;\n" );
                print( STAT "\$stat_numpages=$numpages\;\n" );
	        close( STAT );
            }

	    # Clear error flag
	    $errorstat = 0;
	}
	print( STDERR "Error encountered when creating page\n" )
                if $errorstat;
        ++$pagenum;	# Next page
    }
   
    print( STDERR "\n" ) if $opt_verbose;
    
    #
    # Clean up old files
    #
    &setpagefnames;
    while( -f $pagestat ||
           -f $montageshtml ||
           -f $montagegif ||
           -f $montagejpeg ||
           -f $montagemiff ||
           -f $montagemap ||
           -f $htmlindex
         ) {
        unlink(
                $htmlindex,
                $montagegif,
                $montagejpeg,
                $montagemap,
                $montagemiff,
                $montageshtml,
                $pagestat
        );
        ++$pagenum;	# Next page
        &setpagefnames;
    }

    #
    # Clean up cached thumbnails
    #
    if( $opt_cache && -d $opt_cachedir ) {
        opendir( CACHEDIR, "$opt_cachedir")
            || die("$0: Failed to open directory $opt_cachedir\n$@\n");
        @cachefiles = sort(grep(!/$exclude/,readdir( CACHEDIR )));
        closedir( CACHEDIR );
    }
    {
        my @extra;
        my %tarray;

        grep( $tarray{$_}++, @imgfiles );
        @extra = grep( $_ = "$opt_cachedir/$_", grep( ! $tarray{$_}, @cachefiles ));
        print( STDERR "Removing extra cache files @extra\n") if $opt_debug;
        unlink( @extra );
    }

    #
    # Write out index files (Both main index and frames index files)
    #
    if( $doindexhtml ) {
        &writeindexes;
    }
}

#
# Write out both top index and frame index files
#
sub writeindexes {

    print( STDERR "Writing Index Files ${opt_indexname} & ",
                "${opt_pageindexname}dir.html ...\n" )
                if $opt_debug;

    #---- Generate the Variables for Format Options ----
    #
    # Generate HTML for up link
    #
    local($uphtml) = ('');
    # get indexname of parent directory
    local($indexname) =
             &get_rc_var('..', 'opt_indexname', $opt_indexname);
    unless ( $indexname eq 'NOLINK' ) {
       $uphtml = "<A HREF=\"../${indexname}\">
    <IMG SRC=\"$icon_url{'up'}\" $icon_size{'up'} ALT=\"^\" BORDER=0></A>
    <A HREF=\"../${indexname}\">Up</A><BR>";
    }

    #
    # Generate HTML for help link
    #
    local($helphtml) = ('');
    if( $havereadme ) {
        $helphtml = "<A HREF=\"${opt_readme}\" TARGET=\"pagemap\">
    <IMG SRC=\"$icon_url{'help'}\" $icon_size{'help'} ALT=\"?\" BORDER=0></A>
    <A HREF=\"${opt_readme}\" TARGET=\"pagemap\">ReadMe</A><BR>";
    }

    #
    # Compute HTML for link to first image page
    #
    local($nexthtml) = ('');
    if( $havereadme && $haveimages ) {
       $nexthtml .= "<A HREF=\"${opt_pageindexname}1.html\" TARGET=\"pagemap\">
    <IMG SRC=\"$icon_url{'next'}\" $icon_size{'next'} ALT=\"\" BORDER=0></A>
    <A HREF=\"${opt_pageindexname}1.html\" TARGET=\"pagemap\">Images</A><BR>";
    }

    #
    # Compute HTML for directory list
    #
    local($dirhtml) = ('');
    if( !$opt_prune && scalar(@subdirectories) > 0 ) {
      local($subdir);
      $dirhtml = "<H3>Directories</H3>\n";
      foreach $subdir (sort(@subdirectories)) {

	# If an alternative name is defined, then use it
	if( defined( $dirnames{$subdir} ) ) {
	  $dirtitle=$dirnames{$subdir};
	} else {
	  $dirtitle=$subdir;
	}

	# get indexname for sub-directory (default as this directory)
	local($indexname) =
	   &get_rc_var($subdir, 'opt_indexname', $opt_indexname);

	unless ( $indexname eq 'NOLINK' ) {
	  $dirhtml .= "<NOBR><A HREF=\"${subdir}/${indexname}\">
    <IMG SRC=\"$icon_url{'ball'}\" $icon_size{'ball'} ALT=\"*\" BORDER=0></A>
    <A HREF=\"${subdir}/${indexname}\">$dirtitle</A></NOBR><BR>\n";
	} 
      }
    }

    #
    # Generate HTML for page index list
    #
    local($pageindexhtml) = ('');
    if( $haveimages ) {
        $pageindexhtml = "<H3>Page Navigator</H3>\n";
        for( $i=1; $i <= $numpages; ++$i ) {
            $pageindexhtml .= 
                "  <A HREF=\"${opt_pageindexname}${i}.html\">${i}</A><BR>\n";
        }
    }

    # ----- Evaluate the Format Options -----
    #
    # Evaluate the Top Index File Format Option
    #
    local($indexhtml);
    $indexhtml = eval '"' . $opt_dirfmt . '"';
    die( "Bad Eval of directory page template (\$opt_dirfmt)\n$@\n" )
       if $@;
    
    # Change header to plain bold text for framed directory file
    $dirhtml =~
        s|^<H3>Directories</H3>\n|<B>Directories</B><BR>\n|;
    $pageindexhtml =~
        s|^<H3>Page Navigator</H3>\n|<B>Page Navigator</B><BR>\n|;

    #
    # Evaluate the Framed Directory File Format Option
    #
    local($pagedirhtml, $dirframelink, $pageframelink);
    $pagedirhtml = eval '"' . $opt_frameddirfmt . '"';
    die( "Bad Eval for directory page template (\$opt_frameddirfmt)\n$@\n" )
       if $@; 

    #
    # Evaluate the Frame Format Option
    #
    # Point to directory html
    $dirframelink = "${opt_pageindexname}dir.html";
    if( $havereadme && $readmevisible ) {
    	# Point to README.html
        $pageframelink = $opt_readme;
    } else {
    	# Point to first image page
        $pageframelink = "${opt_pageindexname}1.html";
    }

    local($framespechtml);
    $framespechtml = eval '"' . $opt_framefmt . '"';
    die( "Bad Eval for Frame template (\$opt_framefmt)\n$@\n" )
       if $@; 

    # ----- Output Top Index File (usally "index.html") -------
    #
    open( INDEX, ">${opt_indexname}")
	|| die("$0: Failed to open file ${opt_indexname} for output\n$@\n");
    print( INDEX "<HTML><HEAD>\n",
                 "<TITLE>${title}</TITLE>\n",
                 "</HEAD>\n" );
    print( INDEX $framespechtml );
    print( INDEX "<NOFRAMES>\n",
                 "<BODY TEXT=\"${opt_colorfore}\"",
                " BGCOLOR=\"${opt_colorback}\"\n",
                "    LINK=\"${opt_colorlink}\"",
                " VLINK=\"${opt_colorvlink}\"",
                " ALINK=\"${opt_coloralink}\">");

    print( INDEX $indexhtml );
    print( INDEX "</BODY>\n</NOFRAMES>\n</HTML>" );
    close( INDEX );


    # ----- Output Frame Directory File (usally ".indexdir.html") ------
    #
    open( INDEX, ">${opt_pageindexname}dir.html")
	|| die("$0: Failed to open file \"${opt_pageindexname}dir.html\"",
                " for output\n$@\n");
    print( INDEX "<HTML><HEAD>\n",
                 "<TITLE>${title}</TITLE>\n",
                 "<BASE TARGET=\"_top\">\n",
                 "</HEAD>\n",
                 "<BODY TEXT=\"${opt_dircolorfore}\"",
                 " BGCOLOR=\"${opt_dircolorback}\"\n",
                 "      LINK=\"${opt_dircolorlink}\"",
                " VLINK=\"${opt_dircolorvlink}\"",
                " ALINK=\"${opt_dircoloralink}\">" );

    print( INDEX $pagedirhtml );
    print( INDEX "</BODY></HTML>\n" );
    close( INDEX );

    return ( 0 );
}


#
# Write out page index file
#
sub writeindexfile {
    local(@srcfiles) = @_;	# Source files to process
    local($indexbar);		# HTML text representing numeric selection bar
    local($errorstat)=0;

    print( STDERR "Writing file ${htmlindex} ...\n" )
        if $opt_debug;

    $numimages = scalar(@srcfiles);
    
    # Calculate page index bar
    # No link for current page
    # Nothing at all when there is only one page.
    $indexbar = "<NOBR>\n";

    # --- readme link ---
    if ( $havereadme ) {
	$indexbar .= "<A HREF=\"${opt_readme}\" TARGET=\"pagemap\"
    ><IMG SRC=\"$icon_url{'help'}\" $icon_size{'help'} ALT=\"ReadMe\" BORDER=0></A>\n";
    }
    # --- prev link ---
    if( $pagenum == 1 ) {
        # Go to base index page if on first page
        $indexbar .= "<A HREF=\"${opt_indexname}\" TARGET=\"_top\"
     ><IMG SRC=\"$icon_url{'prev'}\" $icon_size{'prev'} ALT=\"Prev\" BORDER=0></A>\n";
    } else {
        # Go to preceding page
        $indexbar .= "<A HREF=\"${previndex}\" TARGET=\"pagemap\"
    ><IMG SRC=\"$icon_url{'prev'}\" $icon_size{'prev'} ALT=\"Prev\" BORDER=0></A>\n";
    }
    # --- next link ---
    if( $numpages > 1 ) {
	if( $pagenum < $numpages ) {
	    $indexbar .= "<A HREF=\"${nextindex}\" TARGET=\"pagemap\"
    ><IMG SRC=\"$icon_url{'next'}\" $icon_size{'next'} ALT=\"Next\" BORDER=0></A>\n";
	} else {
	    # Print a grayed out arrow to maintain alignment
	    $indexbar .= "<IMG SRC=\"$icon_url{'next_gray'}\" $icon_size{'next_gray'} ALT=\"\" BORDER=0>\n";
        }
    # --- page links ---
	for ($page = 1; $page <= $numpages; ++$page) {
	    if ( $page != $pagenum ) {
                $indexbar .= "<A HREF=\"${opt_pageindexname}${page}.html\" TARGET=\"pagemap\">${page}</A>\n";
	    } else {
        	$indexbar .= " ${page}\n";
	    }
	}
    }
    $indexbar .= "</NOBR>\n";

    
    open( INDEX, ">${htmlindex}")
        || die("$0: Failed to open file ${htmlindex} for output\n$@\n");
    print( INDEX "<HTML><HEAD>\n",
                 "<TITLE>${title}</TITLE>\n",
                 "<BASE TARGET=\"_top\">\n",
                 "</HEAD>\n",
                 "<BODY\n TEXT=\"${opt_colorfore}\"",
                 " BGCOLOR=\"${opt_colorback}\"\n",
                 "        LINK=\"${opt_colorlink}\"",
                 " VLINK=\"${opt_colorvlink}\"",
                 " ALINK=\"${opt_coloralink}\">\n\n");

    # Leave page blank unless there is something to show
    if( $numimages > 0 ) {
        print( INDEX "${opt_header}\n" ) if $opt_header;
	print( INDEX "<H4>Index of files \"$srcfiles[0]\" through",
                        " \"$srcfiles[$numimages-1]\"</H4>\n" );
	print( INDEX "<P>\n$indexbar\n</P>\n" );
	    
	# Determine image name to use
	if( -f $montagegif ) {
	    $imagename = $montagegif;      # Use GIF
	}
	if( -f $montagejpeg ) {
	    $imagename = $montagejpeg;     # Use JPEG
	}

	#
        # Get montage image size
        $imagesize='';
	# Comment out following line if determining montage size is too slow.
        $imagesize=&html_imgsize($imagename) if ( -f $imagename );

	# Add image map info to html file
	if ( -f $montageshtml ) {
	    # Write out client-side imagemap
	    if( open( MAP, "<$montageshtml" ) ) {
		while( <MAP> ) {
		    chop;
		    # Eliminate cache dir from path HACK! HACK!
		    s%href=$opt_cachedir/%href=% if $opt_cache;
		      if(/<map name=([^>]+)/) {
			  $mapname=$1;
			  if ( "${opt_htimage}" ne '' ) {
			      print( INDEX
				    "<A HREF=\"${opt_htimage}${montagemap}\"\n>" );
			} else {
			    print( INDEX
                                "<A HREF=\"" . &basename($montagemap) . "\"\n>");
			}
			print( INDEX
                                "<IMG SRC=\"${imagename}\" ${imagesize}",
                                " USEMAP=#${mapname} BORDER=0 ISMAP>" );
			print( INDEX "</A>\n" );
		    }
		    print( INDEX "$_\n" );
		}
		close( MAP );
	    } else {
		print( STDERR "\nFailed to open file $montageshtml for input\n");
		++$errorstat;
	    }
	} else {
	    print( STDERR "\nInput file $montageshtml is missing\n");
	    ++$errorstat;
	}
    }

    # Print Copyright info on non-blank pages.
    if( $numimages > 0 ) {
       print( INDEX "<P>\n<ADDRESS>\n" );
       if( "${opt_address}" ne '' ) {
           print( INDEX "${opt_address}<BR>\n" );
       }
       print( INDEX "<FONT SIZE=-1>" );
       print( INDEX "Page generated on $calendar_months[$td_month] $td_mday,",
                        " 19${td_year}<BR><HR>\n" );
       print( INDEX "Produced by " );
       print( INDEX "<A HREF=\"http://www.cyberramp.net/~bfriesen/webmagick/\"",
                        ">webmagick</A>" );
       print( INDEX " ${webmagick_revision}, Copyright &copy; Bob Friesenhahn\n" );
       print( INDEX "(<A HREF=\"mailto:bfriesen\@simple.dallas.tx.us\">",
                        "bfriesen\@simple.dallas.tx.us</A>)\n" );
       print( INDEX "</ADDRESS>\n" );
    }

    print( INDEX "</BODY>\n" );
    print( INDEX "</HTML>\n" );

    # Close current HTML index file
    close( INDEX );
    return ( $errorstat );
}


#
# Build montage using PerlMagick
#
sub domontage {
    local(@srcfiles) = @_;
    local($errorstat) = 1;	# Started with "failed" status

    use Image::Magick;

    my (
	$imagename,		# Image name
	$image,			# An individual image
	$montage,		# Montage of images
	$newthumb,		# Set to 1 if new thumbnail
	$status,		# Return status
	$thumbs			# Thumbnail array
	);

    # If we need to, then do the expensive stuff Build index
    # files via PerlMagick's Montage operation. Go out of our
    # way to avoid problems with multi-image files JPEG does not
    # support transparency but we fudge by setting the
    # background color in the JPEG file to the page color.
    # Hopefully browsers that support setting the background
    # also support JPEG.

    unlink(
	   $montagemiff,
	   $montagegif,
	   $montagejpeg,
	   $montageshtml
	   );

    #
    # If caching thumbnails then ensure that directory exists
    #
    mkdir( $opt_cachedir, 0755 ) if ! -d $opt_cachedir;

    # Read images into PerlMagick object
    print( STDERR "\nReading images: ", join(' ', @srcfiles), "\n" )
	if $opt_debug;

    # Allocate thumbnail image
    $thumbs = Image::Magick->new;
         
    # Allocate scratch image
    $image = Image::Magick->new;

MONTAGE:
    {     
READ:       
	foreach $imagename (@srcfiles) {

	    my (
		$width,		# Image width
		$height,	# Image height
		$filesize,	# Image file size
		$magick,	# Image magick
		);
	    #
	    # Handle thumbnail cache
	    #
	    $cachename = "$opt_cachedir/$imagename";

	    $newthumb = 1;	# Start presuming that thumbnail is new
	    # If we are caching and cache thumbnail exists and is newer then use it
	    if ( $opt_cache && -f $cachename && (&fmtime($cachename) >= &fmtime($imagename))) {
		# Read image
		print( STDERR "Reading $cachename ...\n" ) if $opt_debug;
		$status = $image->Read("$cachename");
		if("$status") {
		    warn "$status";
		    undef @$image; # Only delete image data, not object
		    next; # Try to read next image
		}

		# Obtain original image parameters
		my $comment = $image->Get("comment");
		if ($comment =~
		    # xv 3.00 & 3.10 format
		    /IMGINFO:(\d+)x(\d+) (\S+) file\s+\((\d+) bytes\)/ ) {
		    $width = $1;
		    $height = $2;
		    $magick = $3;
		    $filesize = $4;
		} else {
		    print( STDERR "Failed to grock image info from thumbnail ${cachename}!\n",
			  "Remove the cache file and try again ...\n");
		}

		# Indicate that thumbnail came from cache
		$newthumb = 0;
	    } else {
		# Otherwise, read and scale image
		
		# Read image
		print( STDERR "Reading $imagename ...\n" ) if $opt_debug;
		$status = $image->Read($imagename);
		if("$status") {
		    warn "$status";
		    undef @$image; # Only delete image data, not object
		    next; # Try to read next image
		}



		# Scale image and obtain original parameters if not from cache
		if( $newthumb ) {
		    # Obtain image parameters
		    ($width, $height, $filesize, $magick) =
			$image->Get("columns", "rows", "filesize", "magick");

		    print( STDERR "Scaling $imagename ...\n") if $opt_debug;
		    if( $opt_cache ) {
			$status = $image->Zoom(geometry=>"${opt_cachegeom}>");
		    } else {
			$status = $image->Zoom(geometry=>"${opt_thumbgeometry}>");
		    }
		    if("$status") {
			warn "$status";
			undef @$image; # Only delete image data, not object
			next; # Try to read next image
		    }
		}

		# If we are caching, thumbnail is new, and image is
		# large enough, then write it to thumbnail cache
		if( $opt_cache && $newthumb && (($width*$height) > $opt_cachemin)) {

		    my $comment="IMGINFO:${width}x${height} ${magick} file  (${filesize} bytes)";
		    print( STDERR "Applying image comment:\n${comment}\n") if $opt_debug;

		    # Apply comment to thumbnail image
		    $status = $image->Comment( $comment );
		    warn "$status" if "$status";
		    
		    print( STDERR "Writing ${cachename} with format ${opt_cacheformat} ...\n" )
			if $opt_debug;
		    $status = $image->Write("${opt_cacheformat}:${cachename}");
		    warn "$status" if "$status";
		}

	    }

	    #
	    # Set image label
	    #
	    my $label = '';
	    if( defined( $imgnames{$imagename} ) ) {
		# Set image specific label
		$label = $imgnames{$imagename};
	    } else {
		if( $opt_thumblabel ne 'false' ) {
		    # Set default label
		    $label = $opt_thumblabel;
                }
	    }
	    # %f = filename
	    # %m = magick
	    # %w = width
	    # %h = height
	    # %s = scene number
	    # $b = file size
	    if ($label ne '') {
		my $sizestr;
		if( $filesize > 1048576 ) {
		    my $size = int($filesize/1048576);
		    $sizestr = "${size}Mb";
		}
		elsif( $filesize > 9999 ) {
		    my $size = int($filesize/1024);
		    $sizestr = "${size}kb";
		} else {
		    $sizestr = "${filesize}b";
		}
		$label =~ s/%f/$imagename/g;
		$label =~ s/%m/$magick/g;
		$label =~ s/%w/$width/g;
		$label =~ s/%h/$height/g;
		$label =~ s/%b/$sizestr/g;
		$status = $image->Label( $label );
		warn "$status" if "$status";		
	    }

	    #
	    # Add thumbnail to thumbs array
	    #
	    push(@$thumbs, @$image);
	     
	    print( STDERR "Freeing $imagename ...\n") if $opt_debug;
             
	    # Only delete image data, not object
	    undef @$image;
	}
# READ end block

	#
	# Set common image attributes
	#
        if( $opt_thumbbordercolor ne 'false' ) {
	    $thumbs->Set( bordercolor=>$opt_thumbbordercolor );
	    if( "$status" ) {
	        warn "$status";
	        last MONTAGE;
	    }
        }
        if( $opt_thumbmattecolor ne 'false' ) {
	    $thumbs->Set( mattecolor=>$opt_thumbmattecolor );
	    if( "$status" ) {
	        warn "$status";
	        last MONTAGE;
	    }
        }

	#
	# Do the montage
	#
	print( STDERR "Creating montage using options:\n $montageargs\n" )
	    if $opt_debug;

	eval "\$montage = \$thumbs->Montage( $montageargs ) ;";
	if( $@ ) {
	    warn( "$@\n" );
	    last MONTAGE;
	}

	warn "\$thumbs->Montage: $montage" unless ref($montage);
	last MONTAGE unless ref($montage);

	#
	# Write GIF file
	#
	print( STDERR "Writing ${montagegif} ...\n" ) if $opt_debug;
	$montage->Set(interlace=>Line);
	$status = $montage->Write("GIF:${montagegif}");
	warn "$status" if "$status";
	last MONTAGE if "$status";

	# If not doing GIF only, do JPEG	      
	if( ! $opt_forcegif ) {
	    # Only do JPEG if GIF is large.
	    # Most reasonable GIFs are under 30K
	    if( &fsize( $montagegif ) > $opt_maxgif ) {
		# Write JPEG file
		print( STDERR "Writing ${montagejpeg} ...\n" )
		    if $opt_debug;
		$status = $montage->Set(
					interlace=>None, # No Interlace
					quality=>75 # Quality factor 75
					);
		warn "$status" if "$status";
		$status = $montage->Write("JPEG:${montagejpeg}");
		warn "$status" if "$status";
		last MONTAGE if "$status";
	    } else {
		print( STDERR "Avoiding JPEG image since GIF is small enough\n" )
		    if $opt_debug;
	    }
	} else {
	    print( STDERR "Avoiding JPEG image due to forcegif option\n" )
		if $opt_debug;
	}

	# Obtain imagemap information
	unlink( $montageshtml );
	print( STDERR "Writing ${montageshtml} ...\n" ) if $opt_debug;
	# Write SHTML file
	$status = $montage->Write("SHTML:${montageshtml}");
	warn "$status" if "$status";
	last MONTAGE if "$status";
	
	# Decide to use GIF or JPEG version of output depending on size.
	# If there is only one type then no need.
	if( -f $montagegif && -f $montagejpeg ) {
	    if( &fsize($montagegif) > &fsize($montagejpeg) ) {
		print( STDERR "Choosing JPEG since it is smaller\n" )
		    if $opt_debug;
		unlink($montagegif); # Use JPEG
	    } else {
		print( STDERR "Choosing GIF since it is smaller\n" )
		    if $opt_debug;
		unlink($montagejpeg); # Use GIF
	    }
	}
	unlink($montagemiff);
	$errorstat = 0;	# If it made it this far, then no error
    }
# MONTAGE end block
     
    # Delete thumbnails
    undef $thumbs;

    # Delete scratch image
    undef $image;
     
    unlink(
	   $montagemiff,
	   $montagejpeg,
	   $montagegif,
	   $montageshtml
	   ) if $errorstat;
    return( $errorstat );
}

#
# Write out imagemap data
#
sub writeimagemap {
    local($errorstat) = 0;
    # Write out server-side imagemap (CERN or NCSA)
    # This uses the absolute path as the URL or a relative one
    # from the referring URL
    # The server must map URLs specified with filesystem paths
    # or support relative URLs from the referrer (Apache &
    # latest NCSA do.
    print( STDERR "Writing file $montagemap ...\n" ) if $opt_debug;
    if( open( MAP, "<$montageshtml" ) ) {
	open( IMAGEMAP, ">$montagemap" )
                || die("$0: Failed to open file $montagemap for output\n$@\n");
	# default URL
	if ( "${opt_htimage}" ne '' ) {
	    print( IMAGEMAP "default "
                . &abs_path_to_url("${srcdir}/${opt_pageindexname}${pagenum}.html")
                . "\n" );
	} else {
	    print( IMAGEMAP "default ${opt_pageindexname}${pagenum}.html\n" );
	}
	while( <MAP> ) {
	    chop;
	    if( (($url,$x1,$y1,$x2,$y2) =
                /<area href=(.+) shape=rect coords=(\d+),(\d+),(\d+),(\d+)>/) ) {
		$url=&basename($url) if $opt_cache; # HACK: Avoid cache directory path
		if( "$opt_maptype" eq 'ncsa' ) {
		    if ( "${opt_htimage}" ne '' ) {
			print( IMAGEMAP "rect "
                                . &abs_path_to_url("${srcdir}/${url}")
                                . " $x1,$y1 $x2,$y2\n" );
		    } else {
			print( IMAGEMAP "rect ${url} $x1,$y1 $x2,$y2\n" );
		    }
		} elsif ( "$opt_maptype" eq 'cern' ) {
		    if ( "${opt_htimage}" ne '' ) {
			print( IMAGEMAP "rect ($x1,$y1) ($x2,$y2) "
                                . &abs_path_to_url("${srcdir}/${url}")
                                . "\n" );
		    } else {
			print( IMAGEMAP "rect ($x1,$y1) ($x2,$y2) ${url}\n" );
		    }
		} else {
		    die( "\nError: \$opt_maptype must be \"cern\"",
                                " or \"ncsa\".\n" );
		}
	    }
	}
	close( IMAGEMAP );
	close( MAP );
	$errorstat=0;
    } else {
	print( STDERR "\nFailed to open file $montageshtml for input\n");
	$errorstat=1;
    }
    return( $errorstat );
}

#
# Return current directory
#
sub cwd {
    local($_);
    chop($_ = `pwd`);
    return $_;
}

#
# Return size of file in bytes
#
sub fsize {
    my($name) = @_;

    my( $dev, $ino, $mode, $nlink, $uid, $gid, $rdef, $size, $atime,
        $mtime, $ctime,$blksize,$blocks) = lstat($name);
    return $size;
}

#
# Return file modification time
#
sub fmtime {
    my($name) = @_;

    my( $dev, $ino, $mode, $nlink, $uid, $gid, $rdef , $size, $atime,
        $mtime, $ctime , $blksize, $blocks) = lstat($name);
    return $mtime;
}

#
# Return file name portion of path
#
sub basename {
    local($name) = @_;
    $name =~ s:([^\/]*/)+::;
    return($name);
}
 
#
# Return directory name portion of path
#
sub dirname {
    my($name) = @_;
    $name =~ s:(/[^\/]+$)::g;
    return($name);
}

#
# Get the physical path for a specified directory/file path
#
sub lets_get_physical {
    local( $path ) = @_;
    $physical=$path;
    if( -d $path ) {
        local( $savedir ) = &cwd;
        chdir( $path );
        $physical=&cwd;
        chdir( $savedir );
    }
    if( -f $path ) {
        local($dir)=&dirname($path);
	local($fname)=&basename($path);
        local( $savedir ) = &cwd;
        chdir( $dir );
        $physical=&cwd;
        chdir( $savedir );
	$physical .= "/$fname";
    }
    return( $physical );
}

#
# Build a relative path to a file given the absolute physical
# path and the directory location specified by $currentdir
#
# Usage:   abs_path_to_rel($path);
#
# Example:  $relative_icon_path = abs_path_to_rel($absolute_path);
#
sub abs_path_to_rel {
  local($path)	= @_;
  local($dir)	= $currentdir;  # Obtained from global $currentdir
  local(@path,@dir);
  local($savepath);
  
  $savepath=$path;
   
  # Don't do any transformations if either path is not absolute
  # This also avoids paths that contain things like "http://".
  if ( ( $path !~ m|^/| ) || ($dir !~ m|^/|) ) {
      print( "abs_path_to_rel: Not absolute path, returning $savepath\n" )
                if $opt_debug;
      return( $savepath );
  }
  
  if( ! -f $path && ! -d $path ) {
      print( "abs_path_to_rel: Path does not exist, returning $savepath\n" )
                if $opt_debug;
      return( $savepath );
  }
  
  @path=split('/', $path);	# Array form
  shift(@path);
  @dir=split('/', $dir);	# Array form
  shift(@dir);
  
  # If roots are not the same, then return without transformation
  if ( $path[0] ne $dir[0] ) {
      print( "abs_path_to_rel: Roots are not the same, returning $savepath\n" )
                if $opt_debug;
      return( $savepath );
  }
  
  # Remove common start directories
   while( scalar(@path) && scalar(@dir) ) {
      last if( $path[0] ne $dir[0] );
      shift(@path);
      shift(@dir);
   }
   
   # Prepend any ../ part
   grep($_='..',@dir);
   
   # Return results
   if( scalar(@dir) ) {
       return( join('/',@dir,@path) );
   } else {
       if( scalar(@path) ) {
           return( './' . join('/',@path) );
       } else {
       	   return( '.' );
       }
   }
}

#
# Build a relative path to a file given the absolute physical path
# Uses the option variables $opt_rootpath and $opt_prefixpath
#
sub abs_path_to_url {
    local($_) = @_;
    
    # Remove root prefix if absolute
    s|^${opt_rootpath}||;
    
    # Tack on prefix (if any)
    $_ = "${opt_prefixpath}${_}";
    return( $_ );
}

#
# Subroutine to print help message
#
sub help {
    print( STDOUT $help );
}

#
# Subroutine to calculate per-page file names
# This is so names can be defined in one place
#
sub setpagefnames {
    # Run status file
    $pagestat="${opt_pageindexname}${pagenum}.stat";

    # Generated map file
    $montageshtml="${opt_pageindexname}${pagenum}_map.shtml";

    # Generated GIF file
    $montagegif="${opt_pageindexname}${pagenum}.gif";

    # Generated JPEG file
    $montagejpeg="${opt_pageindexname}${pagenum}.jpg";

    # Generated MIFF file
    $montagemiff="/tmp/webmagick${opt_pageindexname}${pagenum}.miff.$$";

    # Generated server-side imagemap file
    $montagemap="${srcdir}/${opt_pageindexname}${pagenum}.map";

    # Montage size cache file
    $montagesize="${opt_pageindexname}${pagenum}.siz";

    # Image map tag name
    $montageusemap="${opt_pageindexname}${pagenum}";

    # Name for current HTML index page
    $htmlindex="${opt_pageindexname}${pagenum}.html";
    
    # Name for next HTML index page
    $nextpagenum=$pagenum + 1;
    $nextindex="${opt_pageindexname}${nextpagenum}.html";
    
    # Name for previous HTML index page
    $prevpagenum=$pagenum - 1;
    $previndex="${opt_pageindexname}${prevpagenum}.html";
}

#
# Escape special characters in HTML text
#
sub escapehtml {
    local($_) = @_;
    s/&/&amp;/g;
    s/>/&gt;/g;
    s/</&lt;/g;
    return( $_ );
}

#
# Escape unsafe characters in URLs
#		
sub escapeurl {
    local($_) = @_;
    s/\%/%25/g;		# % (must substitute first!)
    s/\"/%22/g;		# "
    s/\#/%23/g;		# #
    s/\</%3C/g;		# <
    s/\>/%3E/g;		# >
    s/\[/%5B/g;		# [
    s/\\/%5C/g;		# \
    s/\]/%5D/g;		# ]
    s/\^/%5E/g;		# ^
    s/\`/%60/g;		# `
    s/\{/%7B/g;		# {
    s/\|/%7C/g;		# |
    s/\}/%7D/g;		# }
    s/\~/%7E/g;		# ~
    return( $_ );
}

#
# Convert time in seconds to minutes:seconds.hundreths
#
sub elapsedminutes {
    local($seconds) = @_;
    $min   = int($seconds/60);
    $sec   = int($seconds%60);
    $hund = ($seconds - int($seconds)) * 100;
    return( "${sec}s" ) if $min == 0;
    return( "${min}:" . sprintf( "%02d", $sec ) ) if $hund == 0;
    return( "${min}:" . sprintf( "%02d.%02d", $sec, $hund ) );
}

#
# PERL-based RC file handlers
#

#
# Search for and return the contents of an rc file
# The file handle will be auto-close for next file.
#
sub get_rc {
   local($rc) = @_;
   open( RC, "<${rc}" );
   return join('', <RC>);
}

#
# Eval .webmagickrc files with specified path. If the file does not
# exist or is not readable, then return silently. If an error occurs,
# then print message and return zero to caller (who can die if deemed
# necessary). This allows statements like:
# $source_rc( $rcfile) || die( "Failed to source $rcfile\n" );
#
sub source_rc {
    foreach $rc (@_) {
        if ( -r $rc && -f _ ) {
           print( "Sourcing ${rc}\n" ) if $opt_debug;
           eval ( &get_rc($rc) );
           if( $@ ) {
               print( STDERR "Bad Eval for file \"${rc}\"...\n$@\n" );
               return( 0 );
           }
        }
    }
    return( 1 );
}

#
# Look in the .webmagickrc file for the given directory and return
# the variable requested, or the default value given. this tries to
# be a bit more intelligent than previous eval.
#   -- added by Anthony Thyssen <anthony@cit.gu.edu.au>
#   
sub get_rc_var {
   local($dir, $var, $def) = @_;   
   local($rc) = "$dir/$webmagickrc";
   local($val) = ';' . &get_rc( $rc );
   $val =~ s/#.*//g;   # remove comments to avoid confusion
   if ( $val =~ /\$$var\b/ > 1 ) {
     print STDERR "Var \"\$$var\" is not simple in \"$rc\" -- using default.\n";
     return $def;
   }
   # find variable assignment if pressent and remove stuff before it
   unless ( $val =~ s/[\000-\177]*;\s*\$$var\s*=\s*// ) {
#     print("DB: \$$var not found in \"$rc\"\n")  if $opt_debug;
     return $def;               # variable assignment was not found
   }
   $val =~ s/;[\000-\177]*//;   # remove stuff after assignment expression

   # print("Assignment for \$$var = \"$val\"\n") if $opt_debug;

   $val = eval ( $val );
   if ( $@ ) {
     warn("Bad Eval for variable \"\$$var\" in \"$rc\"...\n$@\n");
     $val = $def;
   }

#   print("DB: \$$var found in \"$rc\" with value of \"$val\"\n")
#               if $opt_debug;
   return  $val;
}

#
# Eval PERL-format rc files in order from $opt_rootpath to
# $currentdir directory. Values are added to global variables
# 
sub eval_rc {
    local($dir)  = $currentdir;	  # current directory
    local(@top,@dir);
    local($rcpath);

    # Decide how far to look for .webmagickrc files
    # Support the case where processing outside of the server
    # root directory.  In that case, use the srcdir instead.
    if( $currentdir =~ m|^$opt_rootpath| ) {
        local($top)  = $opt_rootpath; # Use server root directory
    } else {
        local($top)  = $opt_srcdir; # Use specified source directory
    }

    @top=split('/', $top);	# Array form
    shift(@top);
    
    @dir=split('/', $dir);	# Array form
    shift(@dir);

    splice(@dir, 0, scalar(@top) ); # Leave only subdirectory part

    #
    # Build up path starting at top sourcing any .webmagickrc as we go.
    #
    $path=$top;
    $direlem='';
    do {
	# Certain values must only be vaild in the last
	# current directory webmagickrc file.
        $opt_ignore=0;          # Ignore -- do not process this directory

        if( $direlem ) {
            $path = "$path/$direlem";
        }
        $rcpath = "$path/$webmagickrc";
        &source_rc( $rcpath );
    } while( $direlem = shift(@dir) );

    return( 0 );
}

#
# Lookup color in RGB hash table
#
sub lookuprgb {
    local($color) = @_;
 
    # If already in hex format, don't translate
    if( $color =~ /^\#/ ) {
        return( "\U$color" );   # just uppercase the color hex value
    } 

    if( defined($RGBDB{"\L$color"}) ) {
        return( $RGBDB{"\L$color"} );
    } else {
        print( STDERR "No such color \"$color\" found\n" );
        return("#BEBEBE");    # Return grey as default in case of error
    }
}

#
# Print details regarding executing child process
# Takes return from system() command or $? as input
# Borrowed from example provided in the Camel book
# (PERL 5) page 230
#

sub syserror {
    local($rc) = @_;

    print("Process exited: ");

    $rc = 0xffff & $rc;
    
    if($rc == 0) {
        print("normal exit\n");
    }
    elsif ($rc == 0xff00) {
        print("command failed: $!\n");
    }
    elsif ($rc > 0x80) {
        $rc >>= 8;
	print("ran with non-zero exit status $rc\n");
    }
    else {
        print("ran with ");
	if ($rc &   0x80) {
	    $rc &= ~0x80;
	    print("coredump from ");
	}
	print("signal $rc\n");
    }
}
