#! /bin/sh # This script creates tumbnails, reduced versions of large images, and an # index.html file pointing to all of it. GLOBAL_rcsid='$Id: makethumbs.sh,v 1.272 2007/04/25 06:18:01 molenda Exp $' GLOBAL_rcsrev='$Revision: 1.272 $' # Design goals are # (1) exist in a single script that can be mailed around easily, etc. # (2) generate clean, simple, portable HTML # (3) no databases required # (4) portable portable portable # There are many other programs that compromise some or all of these goals. # They are real neat, they make great web pages, they organize your life. # They don't fit in a single ugly Bourne shell script whose only point in # life is to take pictures off your digital camera and make them basically # presentable to your friends. # This script is in the public domain, share and enjoy. # The latest version is always at http://www.molenda.com/makethumbs/ # Written by Jason Molenda, 1998-09-13; rewritten less lamely 2001-10-26. ## Notes to people reading this script. ## ## Start at main() and trace the function calls from there. Most of ## the people who modify this script could have accomplished everything ## they want through a proper ~/.makethumbsrc. It's up to you. ## As much as I like Bourne shell, I should have just done this in ## perl or python. On the down side, whichever of those I pick, I'll ## hear constant whining from about half of my friends. No one likes ## Bourne shell, so it's the safest language choice. And to be honest, ## I sometimes think it's too easy when you have all those fancy high ## level data types, scoping, abstractions, and regular expression at your ## finger tips -- it's a little more fun when you need to work hard to ## make things work cleanly without a lot of help from the language. ## Return values from functions are prefixed with "RETURN_". ## Command line options are prefixed with "ARGV_". ## Defaults are prefixed with "DEFAULT_" ## Settings from the .makethumbsrc are prefixed with "DOTRC_" ## Globals variables are prefixed with "GLOBAL_". ## GLOBAL_ will hold all the settings that we should act on. These came ## either from a DEFAULT_, ARGV_, or DOTRC_ if the user overrode the DEFAULT_. ## There are a few functionally seperate sections of the script, they start ## with one of these comments: #### Start of real makethumbs image stuff functions #### Index pages functions #### Image filename conversion/test functions #### Image creation functions #### HTML printing functions #### slideshow functions #### Progress printing functions #### checksum checking functions, unmodified file removal #### descriptions.txt support #### dates.txt support #### Get EXIF et al information and add it to slideshow pages #### Try to get image time/date data via a variety of means main () { enable_atexit_trap_handler find_programs set_environment ${1+"$@"} init_defaults read_dotrc_file check_for_necessary_programs make_dir_transversible "." parse_args ${1+"$@"} get_image_list create_descriptions_file create_dates_file analyze_filenames_and_dates reorder_image_list create_generated_files create_index_pages [ $GLOBAL_create_slideshow -eq 1 ] && create_slideshow exit 0 } # DEFAULT_ values are treated as const and are not normally referenced # by functions in this script. Functions should refer to the GLOBAL_ # version -- it will either be set to the DEFAULT_ or to the ARGV_ value # if the user overrides it on the cmd line. init_defaults () { DEFAULT_columns=3 DEFAULT_max_thumb_size=150 DEFAULT_use_two_windows=0 DEFAULT_reduce_big_pics=1 DEFAULT_reduce_trigger_width=1024 DEFAULT_reduce_trigger_height=768 DEFAULT_reduce_width=640 DEFAULT_reduce_height=480 DEFAULT_feed_width=400 DEFAULT_feed_height=400 DEFAULT_create_feed_images=0 DEFAULT_half_text="half" DEFAULT_create_half_images=1 DEFAULT_large_trigger_width=1700 DEFAULT_large_trigger_height=1300 DEFAULT_large_width=1280 DEFAULT_large_height=1024 DEFAULT_create_large_images=1 DEFAULT_remove_originals=0 DEFAULT_rotation_filename="rot-state.txt" DEFAULT_dates_filename="dates.txt" DEFAULT_descriptions_filename="descriptions.txt" DEFAULT_show_progress=1 DEFAULT_show_timings=0 DEFAULT_preferred_image_tools="sips" # or "netpbm" or "imagemagick" DEFAULT_body_tag="<body>" DEFAULT_body_end_tag="</body>" DEFAULT_meta_tag="undef" DEFAULT_html_charset="undef" DEFAULT_index_base_name="index" DEFAULT_html_file_suffix="html" DEFAULT_index_filename="${DEFAULT_index_base_name}.$DEFAULT_html_file_suffix" DEFAULT_print_captions=1 DEFAULT_use_timestamps_as_captions=1 DEFAULT_print_img_size_on_index=0 DEFAULT_print_title_on_index=1 DEFAULT_print_title_on_slideshow=1 DEFAULT_compact_index_page=0 DEFAULT_index_table_spacing="loose" # or "tight" or "none" DEFAULT_compression_level="75" DEFAULT_image_imprinting_text="undef" DEFAULT_boilerplate_footer="undef" DEFAULT_boilerplate_insert_in_head="undef" DEFAULT_boilerplate_end_of_page="undef" DEFAULT_boilerplate_before_title="undef" DEFAULT_boilerplate_after_title="undef" DEFAULT_boilerplate_index_head_stuff="obsolete" DEFAULT_boilerplate_index_insert_in_head="undef" DEFAULT_boilerplate_index_before_title="undef" DEFAULT_boilerplate_index_after_title="undef" DEFAULT_boilerplate_index_after_table_before_indexlinks="undef" DEFAULT_boilerplate_index_end_of_page="undef" DEFAULT_boilerplate_slideshow_insert_in_head="undef" DEFAULT_boilerplate_slideshow_before_title="undef" DEFAULT_boilerplate_slideshow_after_title="undef" DEFAULT_boilerplate_slideshow_end_of_page="undef" DEFAULT_index_page_title_start_html="<h1 align=\"center\">" DEFAULT_index_page_title_end_html="</h1>" DEFAULT_slideshow_page_title_start_html="<h2>" DEFAULT_slideshow_page_title_end_html="</h2>" DEFAULT_single_index_page=0 DEFAULT_rows_per_index_page=10 DEFAULT_usa_specific_date_format_checks=1 DEFAULT_link_to_original_img_on_index=0 DEFAULT_show_image_info=0 DEFAULT_dont_change_file_permissions="obsolete" DEFAULT_change_file_permissions=1 DEFAULT_file_readable_permissions="a+r,a-x" DEFAULT_dir_transversible_permissions="a+x" DEFAULT_preview_mode=0 DEFAULT_create_slideshow=1 DEFAULT_print_img_size_on_slideshow=1 DEFAULT_slideshow_img_size_across_two_lines=1 DEFAULT_slideshow_images_are_clickable=0 DEFAULT_slideshow_print_javascript_navigation=1 DEFAULT_print_directory_title_on_slideshow_pages=1 DEFAULT_slideshow_print_bottom_navlinks=0 DEFAULT_slideshow_previous_pre_link="<h2>[" DEFAULT_slideshow_previous="previous" DEFAULT_slideshow_previous_post_link="]</h2>" DEFAULT_slideshow_next_pre_link="<h2>[" DEFAULT_slideshow_next="next" DEFAULT_slideshow_next_post_link="]</h2>" DEFAULT_slideshow_ret_to_index_pre_link="<h2>[" DEFAULT_slideshow_ret_to_index="index" DEFAULT_slideshow_ret_to_index_post_link="]</h2>" DEFAULT_monthname_01_text="January" DEFAULT_monthname_02_text="February" DEFAULT_monthname_03_text="March" DEFAULT_monthname_04_text="April" DEFAULT_monthname_05_text="May" DEFAULT_monthname_06_text="June" DEFAULT_monthname_07_text="July" DEFAULT_monthname_08_text="August" DEFAULT_monthname_09_text="September" DEFAULT_monthname_10_text="October" DEFAULT_monthname_11_text="November" DEFAULT_monthname_12_text="December" DEFAULT_this_page_created_text="This page @LINKSTART@created@LINKEND@ on @DATE@." DEFAULT_image_set_n_text="Image set @NUMBER@" DEFAULT_image_set_all_text="All in one" DEFAULT_image_xx_of_yy_text="Image @CURRENT@ of @TOTAL@" DEFAULT_reduced_text="reduced" DEFAULT_large_text="large" DEFAULT_original_text="original" DEFAULT_original_image_text="Original image" DEFAULT_date_formatting_text="@MONTH@ @DAY@, @YEAR@" GLOBAL_columns=$DEFAULT_columns GLOBAL_max_thumb_size=$DEFAULT_max_thumb_size GLOBAL_use_two_windows=$DEFAULT_use_two_windows GLOBAL_reduce_big_pics=$DEFAULT_reduce_big_pics GLOBAL_reduce_trigger_height=$DEFAULT_reduce_trigger_height GLOBAL_reduce_trigger_width=$DEFAULT_reduce_trigger_width GLOBAL_reduce_height=$DEFAULT_reduce_height GLOBAL_reduce_width=$DEFAULT_reduce_width GLOBAL_feed_height=$DEFAULT_feed_height GLOBAL_feed_width=$DEFAULT_feed_width GLOBAL_create_feed_images=$DEFAULT_create_feed_images GLOBAL_create_half_images=$DEFAULT_create_half_images GLOBAL_create_large_images=$DEFAULT_create_large_images GLOBAL_large_trigger_height=$DEFAULT_large_trigger_height GLOBAL_large_trigger_width=$DEFAULT_large_trigger_width GLOBAL_large_height=$DEFAULT_large_height GLOBAL_large_width=$DEFAULT_large_width GLOBAL_rotation_filename=$DEFAULT_rotation_filename GLOBAL_dates_filename=$DEFAULT_dates_filename GLOBAL_descriptions_filename=$DEFAULT_descriptions_filename GLOBAL_show_progress=$DEFAULT_show_progress GLOBAL_show_timings=$DEFAULT_show_timings GLOBAL_preferred_image_tools=$DEFAULT_preferred_image_tools GLOBAL_remove_originals=$DEFAULT_remove_originals GLOBAL_body_tag=$DEFAULT_body_tag GLOBAL_body_end_tag=$DEFAULT_body_end_tag GLOBAL_meta_tag=$DEFAULT_meta_tag GLOBAL_html_charset=$DEFAULT_html_charset GLOBAL_index_filename=$DEFAULT_index_filename GLOBAL_index_base_name=$DEFAULT_index_base_name GLOBAL_html_file_suffix=$DEFAULT_html_file_suffix GLOBAL_print_captions=$DEFAULT_print_captions GLOBAL_use_timestamps_as_captions=$DEFAULT_use_timestamps_as_captions GLOBAL_print_img_size_on_index=$DEFAULT_print_img_size_on_index GLOBAL_print_title_on_index=$DEFAULT_print_title_on_index GLOBAL_print_title_on_slideshow=$DEFAULT_print_title_on_slideshow GLOBAL_compact_index_page=$DEFAULT_compact_index_page GLOBAL_index_table_spacing=$DEFAULT_index_table_spacing GLOBAL_compression_level=$DEFAULT_compression_level GLOBAL_image_imprinting_text=$DEFAULT_image_imprinting_text GLOBAL_boilerplate_footer=$DEFAULT_boilerplate_footer GLOBAL_boilerplate_insert_in_head=$DEFAULT_boilerplate_insert_in_head GLOBAL_boilerplate_end_of_page=$DEFAULT_boilerplate_end_of_page GLOBAL_boilerplate_before_title=$DEFAULT_boilerplate_before_title GLOBAL_boilerplate_after_title=$DEFAULT_boilerplate_after_title GLOBAL_boilerplate_index_after_table_before_indexlinks=$DEFAULT_boilerplate_index_after_table_before_indexlinks GLOBAL_boilerplate_index_head_stuff=$DEFAULT_boilerplate_index_head_stuff GLOBAL_boilerplate_index_insert_in_head=$DEFAULT_boilerplate_index_insert_in_head GLOBAL_boilerplate_index_before_title=$DEFAULT_boilerplate_index_before_title GLOBAL_boilerplate_index_after_title=$DEFAULT_boilerplate_index_after_title GLOBAL_boilerplate_index_end_of_page=$DEFAULT_boilerplate_index_end_of_page GLOBAL_boilerplate_slideshow_insert_in_head=$DEFAULT_boilerplate_slideshow_insert_in_head GLOBAL_boilerplate_slideshow_before_title=$DEFAULT_boilerplate_slideshow_before_title GLOBAL_boilerplate_slideshow_after_title=$DEFAULT_boilerplate_slideshow_after_title GLOBAL_boilerplate_slideshow_end_of_page=$DEFAULT_boilerplate_slideshow_end_of_page GLOBAL_index_page_title_start_html=$DEFAULT_index_page_title_start_html GLOBAL_index_page_title_end_html=$DEFAULT_index_page_title_end_html GLOBAL_slideshow_page_title_start_html=$DEFAULT_slideshow_page_title_start_html GLOBAL_slideshow_page_title_end_html=$DEFAULT_slideshow_page_title_end_html GLOBAL_single_index_page=$DEFAULT_single_index_page GLOBAL_rows_per_index_page=$DEFAULT_rows_per_index_page GLOBAL_usa_specific_date_format_checks=$DEFAULT_usa_specific_date_format_checks GLOBAL_link_to_original_img_on_index=$DEFAULT_link_to_original_img_on_index GLOBAL_show_image_info=$DEFAULT_show_image_info GLOBAL_dont_change_file_permissions=$DEFAULT_dont_change_file_permissions GLOBAL_change_file_permissions=$DEFAULT_change_file_permissions GLOBAL_file_readable_permissions=$DEFAULT_file_readable_permissions GLOBAL_dir_transversible_permissions=$DEFAULT_dir_transversible_permissions GLOBAL_preview_mode=$DEFAULT_preview_mode GLOBAL_create_slideshow=$DEFAULT_create_slideshow GLOBAL_print_img_size_on_slideshow=$DEFAULT_print_img_size_on_slideshow GLOBAL_slideshow_img_size_across_two_lines=$DEFAULT_slideshow_img_size_across_two_lines GLOBAL_slideshow_images_are_clickable=$DEFAULT_slideshow_images_are_clickable GLOBAL_slideshow_print_javascript_navigation=$DEFAULT_slideshow_print_javascript_navigation GLOBAL_print_directory_title_on_slideshow_pages=$DEFAULT_print_directory_title_on_slideshow_pages GLOBAL_slideshow_print_bottom_navlinks=$DEFAULT_slideshow_print_bottom_navlinks GLOBAL_slideshow_previous_pre_link=$DEFAULT_slideshow_previous_pre_link GLOBAL_slideshow_previous=$DEFAULT_slideshow_previous GLOBAL_slideshow_previous_post_link=$DEFAULT_slideshow_previous_post_link GLOBAL_slideshow_next_pre_link=$DEFAULT_slideshow_next_pre_link GLOBAL_slideshow_next=$DEFAULT_slideshow_next GLOBAL_slideshow_next_post_link=$DEFAULT_slideshow_next_post_link GLOBAL_slideshow_ret_to_index_pre_link=$DEFAULT_slideshow_ret_to_index_pre_link GLOBAL_slideshow_ret_to_index=$DEFAULT_slideshow_ret_to_index GLOBAL_slideshow_ret_to_index_post_link=$DEFAULT_slideshow_ret_to_index_post_link GLOBAL_monthname_01_text=$DEFAULT_monthname_01_text GLOBAL_monthname_02_text=$DEFAULT_monthname_02_text GLOBAL_monthname_03_text=$DEFAULT_monthname_03_text GLOBAL_monthname_04_text=$DEFAULT_monthname_04_text GLOBAL_monthname_05_text=$DEFAULT_monthname_05_text GLOBAL_monthname_06_text=$DEFAULT_monthname_06_text GLOBAL_monthname_07_text=$DEFAULT_monthname_07_text GLOBAL_monthname_08_text=$DEFAULT_monthname_08_text GLOBAL_monthname_09_text=$DEFAULT_monthname_09_text GLOBAL_monthname_10_text=$DEFAULT_monthname_10_text GLOBAL_monthname_11_text=$DEFAULT_monthname_11_text GLOBAL_monthname_12_text=$DEFAULT_monthname_12_text GLOBAL_this_page_created_text=$DEFAULT_this_page_created_text GLOBAL_image_set_n_text=$DEFAULT_image_set_n_text GLOBAL_image_set_all_text=$DEFAULT_image_set_all_text GLOBAL_image_xx_of_yy_text=$DEFAULT_image_xx_of_yy_text GLOBAL_reduced_text=$DEFAULT_reduced_text GLOBAL_large_text=$DEFAULT_large_text GLOBAL_half_text=$DEFAULT_half_text GLOBAL_original_text=$DEFAULT_original_text GLOBAL_original_image_text=$DEFAULT_original_image_text GLOBAL_date_formatting_text=$DEFAULT_date_formatting_text ### A handful of GLOBAL_'s aren't intended to be overridden by users, ### they're things that are determined at run-time. And here they are. ### They do not have corresponding DEFAULT_ entries. GLOBAL_descriptions_file_validated=0 GLOBAL_descriptions_file_is_invalid=0 GLOBAL_already_failed_guessing_date_from_dir=0 GLOBAL_heuristic_filenames_are_digicam_boring=0 GLOBAL_heuristic_days_are_identical=0 GLOBAL_heuristic_times_are_absent=0 GLOBAL_heuristic_filename_pattern="undef" GLOBAL_total_image_count=0 GLOBAL_no_image_titles_or_descriptions=1 GLOBAL_makethumbs_version=`echo "$GLOBAL_rcsrev" | sed 's,[^0-9.],,g'` GLOBAL_rcsid=`echo "$GLOBAL_rcsid" | sed -e 's,\$Id: ,,' -e 's,molenda Exp.*,,'` } parse_args () { local optname optarg while [ $# -gt 0 ] do optname="`echo $1 | sed 's,=.*,,'`" optarg="`echo $1 | sed 's,^[^=]*=,,'`" case "$1" in --maxthumbsize=* | --max-thumb-size=*) optarg=`echo $optarg | sed 's,[^0-9],,g'` exit_if_empty "$optname" "$optarg" ARGV_max_thumb_size=$optarg GLOBAL_max_thumb_size=$ARGV_max_thumb_size ;; --columns=* | --cols=*) optarg=`echo $optarg | sed 's,[^0-9],,g'` exit_if_empty "$optname" "$optarg" ARGV_columns=$optarg GLOBAL_columns=$optarg ;; --one-index-page* | --one-index*) ARGV_single_index_page=1 GLOBAL_single_index_page=$ARGV_single_index_page ;; --use-two-windows*|--enable-two-windows|--with-two-windows) ARGV_use_two_windows=1 GLOBAL_use_two_windows=$ARGV_use_two_windows ;; --remove-orig*) ARGV_remove_originals=1 GLOBAL_remove_originals=$ARGV_remove_originals ;; --progress) ARGV_show_progress=1 GLOBAL_show_progress=$ARGV_show_progress ;; --quiet | -q) ARGV_show_progress=0 GLOBAL_show_progress=$ARGV_show_progress ;; --disable-reduce|--without-reduce) ARGV_reduce_big_pics=0 GLOBAL_reduce_big_pics=$ARGV_reduce_big_pics ;; --disable-slideshow|--without-slideshow) ARGV_create_slideshow=0 GLOBAL_create_slideshow=$ARGV_create_slideshow ;; --enable-reduce|--with-reduce) ARGV_reduce_big_pics=1 GLOBAL_reduce_big_pics=$ARGV_reduce_big_pics ;; --compact-index|--compact) ARGV_compact_index_page=1 GLOBAL_compact_index_page=$ARGV_compact_index_page ;; --reduce-height=*) optarg=`echo $optarg | sed 's,[^0-9],,g'` exit_if_empty "$optname" "$optarg" ARGV_reduce_height=$optarg GLOBAL_reduce_height=$ARGV_reduce_height ;; --reduce-width=*) optarg=`echo $optarg | sed 's,[^0-9],,g'` exit_if_empty "$optname" "$optarg" ARGV_reduce_width=$optarg GLOBAL_reduce_width=$ARGV_reduce_width ;; --reduce-trigger-height=*) optarg=`echo $optarg | sed 's,[^0-9],,g'` exit_if_empty "$optname" "$optarg" ARGV_reduce_trigger_height=$optarg GLOBAL_reduce_trigger_height=$ARGV_reduce_trigger_height ;; --reduce-trigger-width=*) optarg=`echo $optarg | sed 's,[^0-9],,g'` exit_if_empty "$optname" "$optarg" ARGV_reduce_trigger_width=$optarg GLOBAL_reduce_trigger_width=$ARGV_reduce_trigger_width ;; --compression-level=*|--compression=*) optarg=`echo $optarg | sed 's,[^0-9],,g'` exit_if_empty "$optname" "$optarg" ARGV_compression_level=$optarg GLOBAL_compression_level=$ARGV_compression_level ;; --preview_mode|--preview|--preview-mode|-p) ARGV_preview_mode=1 GLOBAL_preview_mode=1 ;; --clean | --cleanup) do_cleanup exit 0 ;; -h | --help | -v | --version | -V) help exit 1 ;; -*) echo "`basename $0`: ERROR: Unrecognized option \"$1\"" >&2 ;; *) ARGV_image_list="$ARGV_image_list $1" ;; esac shift done handle_complex_variable_settings } help () { # The following exec goop so I don't have to manually redirect every # message to stderr in this function. exec 4>&1 # save stdout fd to fd #4 exec 1>&2 # redirect stdout to stderr cat <<__EOM__ Usage: `basename $0` [options] --compact-index Create a compact index page to pack in lots of thumbnails --maxthumbsize=n Maximum size of thumbnails, in pixels, default $DEFAULT_max_thumb_size --columns=n Number of thumbnails per line, default $DEFAULT_columns --disable-reduce Don't create a reduced image if the picture is large --disable-slideshow Don't create slideshow files --clean Remove all makethumbs-generated files --compression=n Set JPEG compression percentage to n for generated images. Default is 75. Things usually look OK down to the 40-50's. --preview-mode (Or '-p') Preview mode - quick index page generation. Useful for checking rotations, index appearance. --one-index-page Only create a single index page. Default is to put 10 rows on each index page, creating as many pages as necessary. --use-two-windows Bring up a new window to see images, default is `[ $DEFAULT_use_two_windows -eq 1 ] && echo enabled || echo disabled` __EOM__ if [ $DEFAULT_show_progress -eq 1 ] then echo '--quiet Avoid unnecessary output while running' else echo '--progress Show progress updates as the script runs' fi cat <<__EOM__ --remove-originals Remove original images if we make reduced versions. Useful when disk space is limited; default is `[ $DEFAULT_remove_originals -eq 1 ] && echo enabled || echo disabled` WARNING!!! This option *will* remove your original images if reduced images are available! Run this script in a directory of JPEG files to create thumbnail images and HTML pages. Makethumbs will not overwrite any files you've created by hand. Makethumbs will not modify your original images. Makethumbs will not corrupt your precious bodily fluids. You can permanently override options by creating a ~/.makethumbsrc file. Many more features can be tweaked via the .makethumbsrc file. See the documentation on the makethumbs home page for more information. You should not need to modify makethumbs or the HTML it generates. This script written by Jason Molenda, makethumbs(AT)molenda.com. This is version ${GLOBAL_makethumbs_version}. The latest version of this script is always available at http://www.molenda.com/makethumbs/ __EOM__ exec 1>&4 # Copy stdout fd back from temporary save fd, #4 } # RETURN_found is 1 if found, 0 if not found. If found, $RETURN_fullname # contains the path + filename. find_in_path () { local OFS i target dir target="$*" RETURN_fullname="" RETURN_found=0 OFS="$IFS" IFS=: for i in $PATH do [ -z "$i" ] && i="." if [ -f "$i/$target" ] then RETURN_fullname="$i/$target" RETURN_found=1 break fi done IFS="$OFS" } # With a couple of options, setting those options implies a few other # settings. handle_complex_variable_settings () { if [ $GLOBAL_compact_index_page -eq 1 ] && \ [ -n "$DOTRC_compact_index_page" -o -n "$ARGV_compact_index_page" ] then if [ -z "$ARGV_max_thumb_size" -a -z "$DOTRC_max_thumb_size" ] then GLOBAL_max_thumb_size=75 fi if [ -z "$ARGV_print_captions" -a -z "$DOTRC_print_captions" ] then GLOBAL_print_captions=0 fi if [ -z "$ARGV_columns" -a -z "$DOTRC_columns" ] then GLOBAL_columns=6 fi if [ -z "$ARGV_index_table_spacing" -a -z "$DOTRC_index_table_spacing" ] then GLOBAL_index_table_spacing="tight" fi fi if [ $GLOBAL_preview_mode -eq 1 ] && \ [ -n "$DOTRC_preview_mode" -o -n "$ARGV_preview_mode" ] then if [ -z "$ARGV_create_slideshow" -a -z "$DOTRC_create_slideshow" ] then GLOBAL_create_slideshow=0 fi if [ -z "$ARGV_reduce_big_pics" -a -z "$DOTRC_reduce_big_pics" ] then GLOBAL_reduce_big_pics=0 fi if [ -z "$ARGV_create_large_images" -a -z "$DOTRC_create_large_images" ] then GLOBAL_create_large_images=0 fi fi } # Parse a user's $HOME/.makethumbsrc. The code here is kind of # funky because I didn't want to just bourne-shell source the file # (". $HOME/.makethumbsrc"); any syntax error in that startup file # would have caused makethumbs to fail in weird ways and users could have # trouble debugging it. It's a huge amount of work to get all the parsing # correct so spaces, quote marks, apostrophies, etc., are all carried over # accurately. The sed expression where $value gets set is not something # I'm too proud of - this is always the sort of thing that is tricky in # Bourne shell. read_dotrc_file () { local this_dotrc tmpf varname varname_is_valid line value curval for this_dotrc in $HOME/.makethumbsrc $HOME/.makethumbs $HOME/makethumbsrc \ ../../.makethumbsrc ../../.makethumbs ../../makethumbsrc \ ../.makethumbsrc ../.makethumbs ../makethumbsrc \ .makethumbsrc .makethumbs makethumbsrc do [ ! -f "$this_dotrc" ] && continue make_tmpfile dotrc tmpf=$RETURN_tmpfile cat "$this_dotrc" | grep -v '^[ ]*#' | grep = > $tmpf [ ! -s $tmpf ] && continue while read line do varname_is_valid=0 varname=`echo "$line" | sed -e 's,=.*,,' -e 's,[^A-Za-z_0-9],,g' \ -e 's,^ARGV_,,' -e 's,^GLOBAL_,,' \ -e 's,^DEFAULT_,,' -e 's,^DOTRC_,,'` # Eliminate whitespace, quotes around the value value=`echo "$line" | sed -e 's,^[^=]*=,,' -e 's,^[ ]*,,' \ -e 's,[ ]*$,,' \ -e s,^\[\"\'\]\[\ \ \]\*,, \ -e s,\[\ \ \]\*\[\"\'\]\$,,` if echo "$varname" | grep -i '_FILE$' >/dev/null 2>&1 then read_in_file_contents "$value" if [ $RETURN_file_found -eq 1 ] then value=`echo "$RETURN_file_contents" | grep -v '^[ ]*$'` else echo WARNING: Unable to read in file "$value" for variable "$varname" 2>&1 continue fi varname=`echo "$varname" | sed 's,_[Ff][Ii][Ll][Ee]$,,'` fi eval [ -n \"\$GLOBAL_$varname\" ] && varname_is_valid=1 if [ $varname_is_valid -eq 0 ] then echo ERROR: "$this_dotrc" has unrecognized variable name, \"$varname\"!>&2 continue fi # Try to do a little verification if the current value is numeric. I could # probably add some extra checks if the current val is 0 or 1, making an # assumption that it's a boolean value. (someone might try to use "yes" # instead of '1', for instance) curval=`eval echo \\$GLOBAL_$varname` if echo "$curval" | grep '^[0-9]*$' >/dev/null 2>&1 then if [ -z "$value" ] then echo WARNING: Variable $varname currently has a numeric value of $curval >&2 echo WARNING: but you\'re setting it to an empty value. >&2 else if echo "$value" | grep '^[0-9]*$' >/dev/null 2>&1 then : else echo WARNING: Variable $varname currently has a numeric value of $curval >&2 echo WARNING: but you\'re setting it to \"$value\". >&2 fi fi fi eval DOTRC_$varname='$value' eval GLOBAL_$varname=\$DOTRC_$varname done < $tmpf done handle_complex_variable_settings check_for_obsolete_settings check_for_invalid_settings } # A variable might be a filename instead of a value. This function # checks to see if such a file exists and returns the fully qualified # pathname if it does. read_in_file_contents () { local fn i RETURN_file_found=0 RETURN_file_contents="" fn="$*" # Try to expand these via eval if echo "$fn" | egrep '^(~|[$]HOME)' >/dev/null 2>&1 then fn=`eval echo "$fn"` fi # Not all Bourne shells will expand tilde. if echo "$fn" | egrep '^~/' >/dev/null 2>&1 then fn=`echo "$fn" | sed 's,~/,,'` fi if [ -f "$fn" ] then RETURN_file_found=1 RETURN_file_contents=`cat "$fn"` return fi for i in . $HOME .. do if [ -f "$i/$fn" ] then RETURN_file_found=1 RETURN_file_contents=`cat "$i/$fn"` return fi done } # I have to change variable names sometimes, and the warnings and # remappings are all here. check_for_obsolete_settings () { if [ -n "$DOTRC_boilerplate_index_head_stuff" ] && \ [ "$DOTRC_boilerplate_index_head_stuff" != "undef" -a \ "$DOTRC_boilerplate_index_head_stuff" != "obsolete" ] then echo 'WARNING: The variable boilerplate_index_head_stuff has been renamed' 1>&2 echo ' to boilerplate_index_insert_in_head.' 1>&2 DOTRC_boilerplate_index_insert_in_head="$DOTRC_boilerplate_index_head_stuff" GLOBAL_boilerplate_index_insert_in_head="$DOTRC_boilerplate_index_insert_in_head" fi if [ -n "$DOTRC_dont_change_file_permissions" ] && \ [ "$DOTRC_dont_change_file_permissions" != "undef" -a \ "$DOTRC_dont_change_file_permissions" != "obsolete" ] then echo 'WARNING: The variable dont_change_file_permissions has been renamed' 1>&2 echo ' to change_file_permissions, with the opposite setting, for' 1>&2 echo ' consistency. It will be unrecognized in a future version.' 1>&2 if [ $DOTRC_dont_change_file_permissions -eq 1 ] then DOTRC_change_file_permissions=0 else DOTRC_change_file_permissions=1 fi GLOBAL_change_file_permissions=$DOTRC_change_file_permissions echo " e.g. make it \"change_file_permissions=$DOTRC_change_file_permissions\"" 1>&2 fi } check_for_invalid_settings () { if [ -n "$DOTRC_columns" -a "$DOTRC_columns" = "0" ] then echo ERROR: Invalid setting of \"columns\" to zero! Exiting. 1>&2 exit 1 fi } # Returns a temp file in $RETURN_tmpfile. # Takes an optional description name argument. make_tmpfile () { make_tmpfile_in_a_dir "$TMPDIR" "$1" } make_tmpfile_in_cwd () { make_tmpfile_in_a_dir . "$1" } # Returns a temp file in $RETURN_tmpfile. make_tmpfile_in_a_dir () { local base_tmpfile n dir name dir="$1" name="$2" [ -z "$dir" ] && dir="$TMPDIR" if [ $GLOBAL_mktemp_is_present -eq 1 ] then RETURN_tmpfile=`mktemp -q "$dir/makethumbs-${name}.XXXXXXX"` touch "$RETURN_tmpfile" if [ $? -eq 0 -a ! -L "$RETURN_tmpfile" -a -O "$RETURN_tmpfile" ] then make_file_owner_read_writable "$RETURN_tmpfile" add_cleanup "$RETURN_tmpfile" return fi fi base_tmpfile="$dir/makethumbs-${name}.$$" RETURN_tmpfile="$base_tmpfile" n=0 while [ -f "$RETURN_tmpfile" ] do n=`expr $n + 1` RETURN_tmpfile="${base_tmpfile}-$n" done touch "$RETURN_tmpfile" if [ -L "$RETURN_tmpfile" -o ! -O "$RETURN_tmpfile" ] then echo "ERROR: Did someone try to spoof my tmp file \"$RETURN_tmpfile\"?" 1>&2 echo "ERROR: I don't know what's up with that. Aborting." exit 1 fi make_file_owner_read_writable "$RETURN_tmpfile" add_cleanup "$RETURN_tmpfile" return } # Ends program execution if necessary programs can't be found. check_for_necessary_programs () { local missed_something progname t missed_something=0 if [ $GLOBAL_gawk_is_present -eq 1 ] then AWK=gawk else AWK=awk fi # Old fashioned awk's don't seem to have a gensub () function. The one place # where I use will use perl instead. Boo! t=`echo "hi hi hi" | $AWK '{print gensub ("hi", "so &", "g", $0);}' 2>/dev/null` if [ -n "$t" -a "$t" = "so hi so hi so hi" ] || [ $GLOBAL_perl_is_present -eq 0 ] then GLOBAL_date_parser_program="awk" else GLOBAL_date_parser_program="perl" fi # For now, pull these two checks because we can usually get by without them # and older netpbm's don't have them. # $GLOBAL_tifftopnm_is_present -eq 1 -a \ # $GLOBAL_pngtopnm_is_present -eq 1 -a \ if [ $GLOBAL_pnmfile_is_present -eq 1 -a \ $GLOBAL_giftopnm_is_present -eq 1 -a \ $GLOBAL_pnmscale_is_present -eq 1 -a \ $GLOBAL_pnmrotate_is_present -eq 1 ] then GLOBAL_netpbm_is_available=1 else GLOBAL_netpbm_is_available=0 fi if [ $GLOBAL_mogrify_is_present -eq 1 -a \ $GLOBAL_convert_is_present -eq 1 -a \ $GLOBAL_identify_is_present -eq 1 ] then GLOBAL_imagemagick_is_available=1 else GLOBAL_imagemagick_is_available=0 fi if [ $GLOBAL_cjpeg_is_present -eq 1 -a \ $GLOBAL_djpeg_is_present -eq 1 ] then GLOBAL_jpegsrc_is_available=1 else GLOBAL_jpegsrc_is_available=0 fi if [ $GLOBAL_sips_is_present -eq 1 ] then GLOBAL_sips_is_available=1 else GLOBAL_sips_is_available=0 fi if [ -f "/System/Library/ColorSync/Profiles/sRGB Profile.icc" ] then GLOBAL_sips_profile_convert_cmd1="--setProperty" GLOBAL_sips_profile_convert_cmd2="profile" GLOBAL_sips_profile_convert_cmd3="/System/Library/ColorSync/Profiles/sRGB Profile.icc" else GLOBAL_sips_profile_convert_cmd1="" GLOBAL_sips_profile_convert_cmd2="" GLOBAL_sips_profile_convert_cmd3="" fi if [ $GLOBAL_imagemagick_is_available -eq 0 -a \ $GLOBAL_sips_is_available -eq 0 ] then if [ $GLOBAL_jpegsrc_is_available -eq 0 ] then echo ERROR: You need to install the jpeg utilities. >&2 echo ERROR: You may be able to find a copy of this at ftp://ftp.uu.net/graphics/jpeg >&2 echo ERROR: Or check the IJG home page http://www.ijg.org/ >&2 missed_something=1 fi if [ $GLOBAL_netpbm_is_available -eq 0 ] then echo ERROR: You need to install the \"netpbm\" utilities. >&2 echo ERROR: You can find this at http://netpbm.sourceforge.net/ >&2 missed_something=1 fi fi if [ $missed_something -eq 1 ] then echo "" >&2 echo ERROR: You will find all the necessary utilities pre-installed >&2 echo ERROR: on most Linux systems. >&2 exit 1 fi if [ "$GLOBAL_preferred_image_tools" = "sips" -a \ $GLOBAL_sips_is_available -eq 1 ] then GLOBAL_tools_to_use=sips elif [ "$GLOBAL_preferred_image_tools" = "imagemagick" -a \ $GLOBAL_imagemagick_is_available -eq 1 ] then GLOBAL_tools_to_use=imagemagick elif [ "$GLOBAL_preferred_image_tools" = "netpbm" -a \ $GLOBAL_jpegsrc_is_available -eq 1 -a \ $GLOBAL_netpbm_is_available -eq 1 ] then GLOBAL_tools_to_use=netpbm elif [ $GLOBAL_sips_is_available -eq 1 ] then GLOBAL_tools_to_use=sips elif [ $GLOBAL_netpbm_is_available -eq 1 -a \ $GLOBAL_jpegsrc_is_available -eq 1 ] then GLOBAL_tools_to_use=netpbm elif [ $GLOBAL_imagemagick_is_available -eq 1 ] then GLOBAL_tools_to_use=imagemagick else GLOBAL_tools_to_use=undef fi } set_environment () { IFS=" " export IFS TMPDIR=${TMPDIR-/tmp} # Some of the optional programs makethumbs runs suck and will dump # core with the slightest provocation - suppress that if possible. ulimit -c 0 >/dev/null 2>&1 # Make sure dd does sensible things. I can't imagine this doesn't # work, but I'm being paranoid. GLOBAL_dd_works_well=0 if [ $GLOBAL_dd_is_present -eq 1 ] then bytesread=`yes 2>/dev/null | dd bs=1024 count=1 2>/dev/null | wc -c | sed 's,[^0-9],,g'` if [ "$bytesread" -ge 1023 -a "$bytesread" -le 1025 ] then GLOBAL_dd_works_well=1 fi fi # POSIX 1003.2 doesn't guarantee that echo -n works -- some systems (OK, Solaris) # require you to put "\c" at the end of the line to avoid the line wrap. # The general idea here was lifted from autoconf. if [ x`(echo -n foo; echo bar) | grep foobar` = xfoobar ] then ac_n="-n" ac_c="" else ac_n="" if [ x`(echo 'foo\c'; echo bar) | grep foobar` = xfoobar ] then ac_c='\c' fi fi # Detect if the 'local' keyword, a somewhat edgy extension to the Bourne # shell language (har har) is supported. If not, try to run a shell that # does support it. Flow of control won't make it past this part of the # function--it usually will either return or exec another program. testvar=testval check_if_local_supported if [ "x$MAKETHUMBS_AVOID_INF_LOOP" != "x" -a $testvar != testval ] then echo "Hm, I re-ran myself but local still doesn't work. Sigh. We'll see what happens." 1>&2 return fi if [ $testvar = testval ] then return fi for shell in bash ksh do find_in_path $shell if [ $RETURN_found -eq 1 ] then echo "Ignore any warnings about 'local' - will try rerunning under ${shell}." 1>&2 echo "You can skip this rerun step by changing the first line of this script to read" 1>&2 echo "#! $RETURN_fullname" 1>&2 echo "Or leave it as it is -- not a big deal." 1>&2 MAKETHUMBS_AVOID_INF_LOOP=didrun export MAKETHUMBS_AVOID_INF_LOOP exec $shell $0 ${1+"$@"} fi done echo "WARNING: This shell does not seem to support the locak keyword but I" 1>&2 echo " could not find a better shell... things may not work well." 1>&2 } check_if_local_supported () { local testvar testvar=not-testval } # Look around and see what programs are installed in $PATH. find_programs () { local prog varname for prog in metacam jhead dphotox dump-exif gphoto-exifdump exif \ mktemp \ cjpeg djpeg rdjpgcom \ pnmfile giftopnm tifftopnm pngtopnm pnmscale pnmrotate \ jpegtopnm ppmtojpeg \ mogrify identify convert \ dd awk gawk perl sips do varname=`echo $prog | tr -d '-'` find_in_path $prog if [ $RETURN_found -eq 1 ] then eval GLOBAL_${varname}_is_present=1 else eval GLOBAL_${varname}_is_present=0 fi done } enable_atexit_trap_handler () { trap "atexit 0" 0 trap "atexit 1" 1 trap "atexit 2" 2 trap "atexit 15" 15 } add_cleanup () { GLOBAL_cleanuplist="$GLOBAL_cleanuplist $*" } remove_cleanup () { local fn fn="$*" GLOBAL_cleanuplist=`echo $GLOBAL_cleanuplist | sed "s|${fn}||"` } atexit () { trap "" 0 1 2 15 [ -n "$GLOBAL_cleanuplist" ] && rm -f $GLOBAL_cleanuplist if [ -n "$1" -a "$1" != 0 ] then echo "" 1>&2 fi [ -n "$1" ] && exit $1 exit 1 } # Unnecessary paranoia - makethumbs can successfully operate even # if you have a umask of 777 thanks to this function. What the heck. make_file_owner_read_writable () { [ $GLOBAL_change_file_permissions -eq 0 ] && return [ -f "$*" ] && chmod u+rw "$*" } # Only accepts *one* argument, which may contain space chars. make_file_world_readable () { make_file_owner_read_writable "$*" [ $GLOBAL_change_file_permissions -eq 0 ] && return if [ -f "$*" -a -n "$GLOBAL_file_readable_permissions" ] then chmod "$GLOBAL_file_readable_permissions" "$*" fi } # Only accepts *one* argument, which may contain space chars. make_dir_transversible () { [ $GLOBAL_change_file_permissions -eq 0 ] && return if [ -d "$*" -a -n "$GLOBAL_dir_transversible_permissions" ] then chmod "$GLOBAL_dir_transversible_permissions" "$*" fi } exit_if_empty () { local desc val desc="$1" shift val="$*" if [ -z "$val" ] then echo ERROR: No argument given with \"$desc\" command line argument! >&2 exit 1 fi } do_cleanup () { local fn indexes get_image_list move_to_backup_file "$GLOBAL_descriptions_filename" move_to_backup_file "$GLOBAL_dates_filename" indexes=`ls ${GLOBAL_index_base_name}.${GLOBAL_html_file_suffix} \ ${GLOBAL_index_base_name}-all.${GLOBAL_html_file_suffix} \ ${GLOBAL_index_base_name}-[0-9].${GLOBAL_html_file_suffix} \ ${GLOBAL_index_base_name}-[0-9][0-9].${GLOBAL_html_file_suffix} \ ${GLOBAL_index_base_name}-[0-9][0-9][0-9].${GLOBAL_html_file_suffix} \ 2>/dev/null` if [ -n "$indexes" ] then for fn in $indexes do remove_or_move_file_aside "$fn" done fi remove_existing_slideshow_html_files while read fn do source_name_to_half_name "$fn" [ "$fn" != "$RETURN_half_name" ] && rm -f "$RETURN_half_name" source_name_to_large_name "$fn" [ "$fn" != "$RETURN_large_name" ] && rm -f "$RETURN_large_name" source_name_to_reduced_name "$fn" [ "$fn" != "$RETURN_reduced_name" ] && rm -f "$RETURN_reduced_name" source_name_to_feed_name "$fn" [ "$fn" != "$RETURN_feed_name" ] && rm -f "$RETURN_feed_name" source_name_to_thumb_name "$fn" [ "$fn" != "$RETURN_thumb_name" ] && rm -f "$RETURN_thumb_name" done < $GLOBAL_image_list_tmpfile } remove_or_move_file_aside () { local fn fn="$*" [ ! -f "$fn" ] && return remove_file_if_unmodified "$fn" [ ! -f "$fn" ] && return move_to_backup_file "$fn" } move_to_backup_file () { local fn i most_recent_backup fn="$*" [ ! -f "${fn}" ] && return if [ ! -f "${fn}~" ] then mv "$fn" "${fn}~" return fi most_recent_backup=`ls -1t "${fn}~" ${fn}~* 2>/dev/null | head -1` if cmp "${fn}" "$most_recent_backup" >/dev/null 2>&1 then rm -f "${fn}" touch "$most_recent_backup" return fi i=1 while [ -f "${fn}~${i}~" -a $i -le 50 ] do i=`expr $i + 1` done if [ ! -f "${fn}~${i}~" ] then mv "$fn" "${fn}~${i}~" return fi } ######################################################### #### Start of real makethumbs image stuff functions ######################################################### # Sets file at $GLOBAL_image_list_tmpfile with images to process. # Tries to normalize filename extensions. get_image_list () { local prelim_image_tmpfile i normalized_filename filename local t make_tmpfile first-image-list prelim_image_tmpfile="$RETURN_tmpfile" make_tmpfile image-list GLOBAL_image_list_tmpfile="$RETURN_tmpfile" if [ -n "$ARGV_image_list" ] then for i in $ARGV_image_list do echo $i >> $prelim_image_tmpfile done else ls -1 | egrep -i '\.jpg$|\.jpeg$|\.png$|\.gif$' > $prelim_image_tmpfile fi cat $prelim_image_tmpfile | while read filename do [ -s "$filename" ] || continue if echo "$filename" | egrep -- '-[bfhtrl].(jpg|gif|png)$' >/dev/null 2>&1 then continue fi normalized_filename=`echo "$filename" | sed -e 's,.JPG$,.jpg,' -e 's,.jpeg$,.jpg,' -e 's,.JPEG$,.jpg,' \ -e 's,.PNG$,.png,' -e 's,.GIF$,.gif,'` move_a_file_in_the_face_of_adversity "$filename" "$normalized_filename" if [ $RETURN_new_and_old_are_identical -eq 0 -a \ $RETURN_did_move_file -eq 0 ] then echo "WARNING: Unable to move \"$filename\" to \"$normalized_filename\"!" 1>&2 if [ $RETURN_new_file_exists -eq 1 -a $RETURN_old_file_exists -eq 1 ] then echo "WARNING: In fact, both seem to exist. Yech! Will try to continue." 1>&2 continue fi fi if [ $RETURN_new_file_exists -eq 1 ] then filename="$normalized_filename" fi make_file_world_readable "$filename" echo "$filename" >> $GLOBAL_image_list_tmpfile done if [ ! -s "$GLOBAL_image_list_tmpfile" ] then echo ERROR: No images found! >&2 exit 1 fi } # The silly way I move files via a temp file is for case-preserving # but case-insensitive filesystems like MacOS's HFS+ or Windows' NTFS. # On those, doing "mv foo.JPG foo.jpg" is an error, and testing for either # file will return true all the time. Sigh. # The hokey stuff with looking at file-basename.[jJpPgG][pPnNiI][gGgGfF] # is because these filesystems will, when you do # ls -1 foo.JPG | egrep foo.JPG # return foo.JPG regardless of whether the file is called foo.jpg or foo.JPG. # How lame. Using a glob tricks it into returning the true extension. move_a_file_in_the_face_of_adversity () { local src dest t tfile local src_egrep dest_egrep src_basename dest_basename RETURN_did_move_file=0 RETURN_old_file_exists=0 RETURN_new_file_exists=0 RETURN_new_and_old_are_identical=0 src="$1" dest="$2" quote_egrep_chars "$src" src_egrep="$RETURN_egrep" quote_egrep_chars "$dest" dest_egrep="$RETURN_egrep" src_basename=`echo "$src" | sed 's,\.[jpgpnggifJPGPNGGIF]*$,,'` if [ "$src" != "$dest" ] then dest_basename=`echo "$dest" | sed 's,\.[jpgpnggifJPGPNGGIF]*$,,'` t=`ls -1 "${dest_basename}."[jJpPgG][pPnNiI][gGgGfF] 2>/dev/null | egrep "$dest_egrep"` if [ -z "$t" ] then make_tmpfile_in_cwd filemover remove_cleanup "$RETURN_tmpfile" # don't remove this one... tfile="$RETURN_tmpfile" mv "$src" "$tfile" && mv "$tfile" "$dest" && RETURN_did_move_file=1 fi t=`ls -1 "${src_basename}."[jJpPgG][pPnNiI][gGgGfF] 2>/dev/null | egrep "$src_egrep"` [ -n "$t" ] && RETURN_old_file_exists=1 t=`ls -1 "${dest_basename}."[jJpPgG][pPnNiI][gGgGfF] 2>/dev/null | egrep "$dest_egrep"` [ -n "$t" ] && RETURN_new_file_exists=1 else RETURN_new_and_old_are_identical=1 t=`ls -1 "${src_basename}."[jJpPgG][pPnNiI][gGgGfF] 2>/dev/null | egrep "$src_egrep"` [ -n "$t" ] && RETURN_old_file_exists=1 [ -n "$t" ] && RETURN_new_file_exists=1 fi } # Use the order of filenames in the descriptions.txt [captions] section # to determine the order that they'll be presented on the web page. reorder_image_list () { local newlist fn newsize oldsize get_caption_filenames_from_descriptions_file [ -z "$RETURN_captions_tmpfile" ] && return [ ! -s "$RETURN_captions_tmpfile" ] && return make_tmpfile newlist newlist="$RETURN_tmpfile" while read fn do quote_egrep_chars "$fn" if egrep "^${RETURN_egrep}\$" $newlist >/dev/null 2>&1 then continue fi if egrep "^${RETURN_egrep}\$" $GLOBAL_image_list_tmpfile >/dev/null 2>&1 then echo "$fn" >> $newlist fi done < $RETURN_captions_tmpfile while read fn do quote_egrep_chars "$fn" if egrep "^${RETURN_egrep}\$" $newlist >/dev/null 2>&1 then : else echo "$fn" >> $newlist fi done < $GLOBAL_image_list_tmpfile newsize=`ls -l $newlist | awk '{print $5}'` oldsize=`ls -l $GLOBAL_image_list_tmpfile | awk '{print $5}'` # I don't know... is there a case where the new one could be smaller? # Maybe a duplicate could be in the original list, and that'd be eliminated # by the reordering. I'll skip the obvious check for now. if [ -n "$newsize" -a -n "$oldsize" -a $newsize -gt 0 ] then cat $newlist > $GLOBAL_image_list_tmpfile fi } iterate_over_image_list () { local col first_row fn filelist indexno indexno="$1" col=0 first_row=1 GLOBAL_last_row_was_closed=0 make_tmpfile thislist-$indexno filelist="$RETURN_tmpfile" if [ "$indexno" = "allinone" ] then cat $GLOBAL_image_list_tmpfile > $filelist else grep "^$indexno " $GLOBAL_image_indexpages | sed 's,^[0-9]* ,,' > $filelist fi while read fn do if [ $col -eq 0 ] then print_html_row_start GLOBAL_last_row_was_closed=0 fi print_html_column_start print_image_entry "$fn" print_html_column_end col=`expr $col + 1` if [ $col -eq $GLOBAL_columns ] then print_html_row_end col=0 GLOBAL_last_row_was_closed=1 fi done < $filelist } print_image_entry () { local fn thumb_width thumb_height was_reduced fn="$*" source_name_to_thumb_name "$fn" get_dimensions "$RETURN_thumb_name" thumb_width=$RETURN_width thumb_height=$RETURN_height reduced_file_exists "$fn" was_reduced=$RETURN_reduced_exists print_image_link $thumb_width $thumb_height $was_reduced "$fn" print_image_size "$fn" } # Only remove JPG images. Removing GIF/PNG images is a little tricky - # see the RCS comment on rev 1.60 for more details on why this is tricky. # FIXME: This function is incorrect given 'large' images - they should # become the plain files if they exist, not the reduced ones. maybe_remove_original () { local name reduced_name large_name old_name local quote_egrep_chars t name="$*" RETURN_original_removed=0 [ $GLOBAL_remove_originals -eq 0 ] && return [ ! -f "$name" ] && return if echo "$name" | grep -v '\.jpg$' > /dev/null 2>&1 then return fi # Have we already removed this file in the past? i.e. the "original" # that we're currently looking at is actually a reduced/large/half image # that was promoted to "original" in a previous --remove-originals run. if [ -f removed-file-list.txt ] then quote_egrep_chars "$name" t=`egrep "^${RETURN_egrep}\$" removed-file-list.txt` if [ -n "$t" ] then return fi fi source_name_to_reduced_name "$name" reduced_name="$RETURN_reduced_name" source_name_to_large_name "$name" large_name="$RETURN_large_name" source_name_to_half_name "$name" half_name="$RETURN_half_name" if [ -f "$half_name" -a -s "$half_name" ] then mv "$name" "${name}.bak" mv "$half_name" "$name" old_name="$half_name" elif [ -f "$large_name" -a -s "$large_name" ] then mv "$name" "${name}.bak" mv "$large_name" "$name" old_name="$large_name" elif [ -f "$reduced_name" -a -s "$reduced_name" ] then mv "$name" "${name}.bak" mv "$reduced_name" "$name" old_name="$reduced_name" fi if [ ! -f "$old_name" -a -f "$name" -a -s "$name" ] then rm -f "${name}.bak" RETURN_original_removed=1 if [ ! -f removed-file-list.txt ] then touch removed-file-list.txt make_file_owner_read_writable removed-file-list.txt echo "# This is a list of images whose original versions have been removed." >> removed-file-list.txt echo "# makethumbs.sh maintains this so multiple uses of --remove-originals" >> removed-file-list.txt echo "# won't continue removing reduced versions of images." >> removed-file-list.txt fi echo "$name" >> removed-file-list.txt else echo ERROR: I got confused while trying to remove original file >&2 echo ERROR: \"$name\"! Aborting... >&2 exit 1 fi } # A little complicated - parameters are # THUMBNAIL-WIDTH THUMBNAIL-HEIGHT REDUCED-IMG-MADE? FILENAME # $thumb_width $thumb_height $was_reduced "$fn" print_image_link () { local twidth theight reduced fn caption thumb_name local slideshow_name reduced_slideshow_name local image_name reduced_name main_link_href secondary_link_href local href_title local t twidth=$1; shift theight=$1; shift reduced=$1; shift fn="$*" get_image_caption_for_main_index "$fn" caption="$RETURN_caption" if [ "$RETURN_caption_was_from" = "descriptions-file" ] then quote_html_chars "$caption" href_title="title=\"$RETURN_html\" " else href_title="" fi source_name_to_thumb_name "$fn" thumb_name="$RETURN_thumb_name" if [ $GLOBAL_create_slideshow -eq 1 ] then image_name_to_html_name "$fn" slideshow_name="$RETURN_html_name" source_name_to_reduced_html_name "$fn" reduced_slideshow_name="$RETURN_reduced_html_name" fi image_name="$fn" source_name_to_reduced_name "$image_name" reduced_name="$RETURN_reduced_name" # The logic ahead is complicated. We change our behavior depending # on whether (a) this image has a reduced version ($reduced), whether # (b) we are making slideshows ($GLOBAL_create_slideshow), and whether # (c) there are any reduced images present. # One odd case is if we are making a slideshow, and we are making reduced # versions of images, but _this_ particular image didn't have a reduced # version. In this one case, the _main_ link we emit is to the reduced # HTML slideshow page. That way when people do Next/Previous, they'll # proceed to the Reduced version (the one they most likely want). if [ $reduced -eq 1 ] then if [ $GLOBAL_create_slideshow -eq 1 ] then main_link_href="$reduced_slideshow_name" secondary_link_href="$slideshow_name" else main_link_href="$reduced_name" secondary_link_href="$image_name" fi else if [ $GLOBAL_create_slideshow -eq 1 ] then if [ $GLOBAL_reduce_big_pics -eq 1 -a \ $GLOBAL_there_is_at_least_one_reduced_img -eq 1 ] then main_link_href="$reduced_slideshow_name" else main_link_href="$slideshow_name" fi else main_link_href="$image_name" fi fi echo $ac_n " <a ${href_title}$ac_c" [ $GLOBAL_use_two_windows -eq 1 ] && echo $ac_n "target=\"display\" $ac_c" quote_url_chars "$main_link_href"; main_link_href="$RETURN_url" quote_url_chars "$thumb_name"; thumb_name="$RETURN_url" echo $ac_n "href=\"$main_link_href\"><img src=\"$thumb_name\" $ac_c" echo $ac_n "width=\"$twidth\" height=\"$theight\" $ac_c" echo $ac_n "alt=\"\" /></a>$ac_c" if [ $GLOBAL_print_captions -eq 1 ] then echo "<br />" echo $ac_n " <a href=\"$main_link_href\">$caption</a>$ac_c" if [ $reduced -eq 1 -a \ $GLOBAL_create_slideshow -eq 1 -a \ $GLOBAL_link_to_original_img_on_index -eq 1 ] then quote_url_chars "$secondary_link_href" t=`echo "$GLOBAL_original_image_text" | sed 's, ,\ ,g'` echo $ac_n "<br /><a href=\"$RETURN_url\">[${t}]</a>$ac_c" fi fi echo "" } print_image_size () { if [ $GLOBAL_print_captions -eq 1 -a \ $GLOBAL_print_img_size_on_index -eq 1 ] then return_image_size "$*" echo " (${RETURN_image_size_str})" fi } return_image_size () { local bytes kbytes fn RETURN_image_size_str="" fn="$*" bytes=`ls -l "$fn" | awk '{print $5}'` kbytes=`expr $bytes / 1000` RETURN_image_size_str="${kbytes}k" } foo_to_pnm () { case "$*" in *.jpg) RETURN_foo_to_pnm="djpeg -ppm";; *.gif) RETURN_foo_to_pnm="giftopnm";; *.tif) RETURN_foo_to_pnm="tifftopnm";; *.png) RETURN_foo_to_pnm="pngtopnm";; *) echo ERROR: Unable to proceed with file "$*"! >&2 exit 1;; esac } ######################################################### #### Index pages functions ######################################################### create_index_pages () { local t index_pages thumbnails_per_page if [ $GLOBAL_single_index_page -eq 1 ] then thumbnails_per_page="$GLOBAL_total_image_count" else thumbnails_per_page=`expr $GLOBAL_rows_per_index_page \* $GLOBAL_columns` fi compute_number_of_indexes index_pages="$RETURN_number_of_index_pages" make_tmpfile indexpages GLOBAL_image_indexpages="$RETURN_tmpfile" set_up_indexpages_tmpfile $index_pages $thumbnails_per_page t=1 while [ $t -le $index_pages ] do create_an_index_page $t $index_pages t=`expr $t + 1` done if [ $GLOBAL_single_index_page -eq 0 -a $index_pages -gt 1 ] then create_an_index_page "allinone" $index_pages fi } compute_number_of_indexes () { local thumbnails_per_page index_pages RETURN_number_of_index_pages="" if [ $GLOBAL_single_index_page -eq 1 ] then RETURN_number_of_index_pages=1 return fi thumbnails_per_page=`expr $GLOBAL_rows_per_index_page \* $GLOBAL_columns` index_pages=`expr $GLOBAL_total_image_count / $thumbnails_per_page + 1` if [ `expr $GLOBAL_total_image_count % $thumbnails_per_page` -le \ `expr $GLOBAL_columns \* 1` -a $index_pages -gt 1 ] then RETURN_number_of_index_pages=`expr $index_pages - 1` else RETURN_number_of_index_pages="$index_pages" fi } set_up_indexpages_tmpfile () { local index_pages thumbnails_per_page local i cur_page fn index_pages="$1" thumbnails_per_page="$2" cur_page=1 i=1 while read fn do [ $cur_page -gt $index_pages ] && cur_page=$index_pages echo "$cur_page" "$fn" >> $GLOBAL_image_indexpages if [ `expr $i % $thumbnails_per_page` -eq 0 ] then i=1 cur_page=`expr $cur_page + 1` else i=`expr $i + 1` fi done < $GLOBAL_image_list_tmpfile } create_an_index_page () { local indexno page_name index_pages indexno="$1" index_pages="$2" if [ -z "$indexno" -o -z "$index_pages" ] then echo ERROR: Unable to understand index page number because it\'s empty 1>&2 exit 1 fi if [ "$indexno" = 1 -o $GLOBAL_single_index_page -eq 1 ] then page_name="${GLOBAL_index_base_name}.${GLOBAL_html_file_suffix}" elif [ "$indexno" = "allinone" ] then page_name="${GLOBAL_index_base_name}-all.${GLOBAL_html_file_suffix}" else page_name="${GLOBAL_index_base_name}-${indexno}.${GLOBAL_html_file_suffix}" fi remove_or_move_file_aside "$page_name" progress_update_start_creating_index_html "$page_name" point_stdout_to_an_index_file "$page_name" print_html_header print_index_list "$indexno" $index_pages print_html_table_start iterate_over_image_list "$indexno" print_html_table_end print_index_list "$indexno" $index_pages print_html_footer "$page_name" progress_update_done_creating_index_html } print_index_list () { local indexno index_pages i page t local pad_for_one_digit pad_for_two_digits padding indexno="$1" index_pages="$2" [ "$indexno" = 1 -a $index_pages -eq 1 ] && return [ $GLOBAL_single_index_page -eq 1 ] && return case $index_pages in [0-9][0-9][0-9]) pad_for_one_digit="\\ \\ " pad_for_two_digits="\\ " ;; [0-9][0-9]) pad_for_one_digit="\\ " pad_for_two_digits="" ;; [0-9]) pad_for_one_digit="" pad_for_two_digits="" ;; *) pad_for_one_digit="" pad_for_two_digits="" ;; esac echo '<blockquote><b>' echo $ac_n " $ac_c" i=1 while [ $i -le $index_pages ] do case $i in [0-9]) padding="$pad_for_one_digit" ;; [0-9][0-9]) padding="$pad_for_two_digits";; [0-9][0-9][0-9]) padding="";; *) padding="";; esac if [ $i -eq 1 ] then page="${GLOBAL_index_base_name}.${GLOBAL_html_file_suffix}" else page="${GLOBAL_index_base_name}-${i}.${GLOBAL_html_file_suffix}" fi [ $i != "$indexno" ] && echo $ac_n "<a href=\"$page\">$ac_c" t=`echo "$GLOBAL_image_set_n_text" | sed -e 's, ,\ ,g' -e "s,@NUMBER@,<tt>${padding}${i}</tt>,"` echo $ac_n "[${t}]$ac_c" [ $i != "$indexno" ] && echo $ac_n "</a>$ac_c" echo $ac_n " $ac_c" i=`expr $i + 1` done if [ $indexno != "allinone" ] then echo $ac_n "<a href=\"${GLOBAL_index_base_name}-all.${GLOBAL_html_file_suffix}\">$ac_c" fi if [ -n "$GLOBAL_image_set_all_text" ] then t=`echo "$GLOBAL_image_set_all_text" | sed 's, ,\ ,g'` echo $ac_n "[${t}]$ac_c" fi if [ $indexno != "allinone" ] then echo '</a>' else echo "" fi echo '</b></blockquote>' echo "<p />" } point_stdout_to_an_index_file () { local fn fn="$*" if [ -f "$fn" ] then echo ERROR: I was unable to remove an index page somehow! Exiting. 1>&2 exit 1 fi GLOBAL_print_html_header_footer=1 exec > "$fn" add_cleanup "$fn" make_file_world_readable "$fn" } # Given an image name, return the name of the index.html page with that # image on it. map_image_to_index () { local fn i page_name fn="$*" RETURN_index_name="" generated_name_to_source_name "$fn" quote_egrep_chars "$RETURN_source_name" indexno=`egrep "^[0-9]* ${RETURN_egrep}\$" $GLOBAL_image_indexpages | head -1 | sed 's, .*,,' | sed 's,[^0-9],,g'` if [ -z "$indexno" ] then echo ERROR: Unable to figure index page for image \"$fn\"! 1>&2 exit 1 fi if [ $indexno -eq 1 ] then page_name="${GLOBAL_index_base_name}.${GLOBAL_html_file_suffix}" else page_name="${GLOBAL_index_base_name}-${indexno}.${GLOBAL_html_file_suffix}" fi RETURN_index_name="$page_name" } ######################################################### #### Image filename conversion/test functions ######################################################### source_name_to_thumb_name () { source_name_to_generated_file_name thumb t "$*" RETURN_thumb_name="$RETURN_generated_name" } source_name_to_feed_name () { source_name_to_generated_file_name feed f "$*" RETURN_feed_name="$RETURN_generated_name" } source_name_to_reduced_name () { source_name_to_generated_file_name reduced r "$*" RETURN_reduced_name="$RETURN_generated_name" } source_name_to_large_name () { source_name_to_generated_file_name large l "$*" RETURN_large_name="$RETURN_generated_name" } source_name_to_half_name () { source_name_to_generated_file_name half h "$*" RETURN_half_name="$RETURN_generated_name" } # Call it like # source_name_to_generated_name large l foo.jpg # to get back "foo-l.jpg" source_name_to_generated_file_name () { local type ext fn RETURN_generated_name="" type="$1"; shift ext="$1"; shift fn="$*" RETURN_generated_name=`echo "$*" | sed "s,\.[^.]*\$,-${ext}.jpg,"` if [ "$fn" = "$RETURN_generated_name" ] then echo ERROR: I couldn\'t create a $type name for "\"${fn}\""! >&2 exit 1 fi if echo "$RETURN_generated_name" | egrep -- "-${ext}-${ext}\." >/dev/null 2>&1 then echo ERROR: I couldn\'t create a $type name for "\"${fn}\""! >&2 exit 1 fi } is_feed_name () { RETURN_is_feed_name=0 if echo "$*" | egrep -- '-f\.(jpg|gif|png)$' >/dev/null 2>&1 then RETURN_is_feed_name=1 fi } is_reduced_name () { RETURN_is_reduced_name=0 if echo "$*" | egrep -- '-r\.(jpg|gif|png)$' >/dev/null 2>&1 then RETURN_is_reduced_name=1 fi } is_large_name () { RETURN_is_large_name=0 if echo "$*" | egrep -- '-l\.(jpg|gif|png)$' >/dev/null 2>&1 then RETURN_is_large_name=1 fi } is_half_name () { RETURN_is_half_name=0 if echo "$*" | egrep -- '-h\.(jpg|gif|png)$' >/dev/null 2>&1 then RETURN_is_half_name=1 fi } is_generated_name () { RETURN_is_generated_name=0 if echo "$*" | egrep -- '-[bfhtrl]\.(jpg|gif|png)$' >/dev/null 2>&1 then RETURN_is_generated_name=1 fi } source_name_to_source_html_name () { RETURN_source_html_name=`echo "$*" | sed "s,\.[^.]*\$,.${GLOBAL_html_file_suffix},"` if [ "$*" = "$RETURN_source_html_name" ] then echo ERROR: I couldn\'t create an HTML name for "\"$*\""! >&2 exit 1 fi } image_name_to_html_name () { RETURN_html_name=`echo "$*" | sed "s,\.[^.]*\$,.${GLOBAL_html_file_suffix},"` if [ "$*" = "$RETURN_html_name" ] then echo ERROR: I couldn\'t create an HTML name for "\"$*\""! >&2 exit 1 fi } html_name_to_image_name () { local fn basename imgname fn="$*" basename=`echo "$fn" | sed -e "s,-[bfhtrl].${GLOBAL_html_file_suffix}\$,," -e "s,.${GLOBAL_html_file_suffix}\$,,"` quote_egrep_chars "$basename" imgname=`egrep "^${RETURN_egrep}.(jpg|gif|png)\$" $GLOBAL_image_list_tmpfile | head -1` RETURN_image_name="$imgname" if [ ! -f "$RETURN_image_name" ] then echo ERROR: I couldn\'t guess the image name for HTML file "\"$*\""! >&2 exit 1 fi } # We may have a reduced image, or we may not. If the reduced image # is present, we return the source image HTML name (so # source_name_to_source_html_name is equivalent to this function), # otherwise we return the reduced image HTML name. source_name_to_reduced_html_name () { local reduced_name source_name_to_reduced_name "$*" reduced_name="$RETURN_reduced_name" RETURN_reduced_html_name=`echo "$reduced_name" | sed "s,\.[jpgpnggif]*\$,.${GLOBAL_html_file_suffix},"` if [ "$reduced_name" = "$RETURN_reduced_html_name" ] then echo ERROR: I couldn\'t create an HTML name for "\"$reduced_name\""! >&2 exit 1 fi } source_name_to_large_html_name () { local large_name source_name_to_large_name "$*" large_name="$RETURN_large_name" RETURN_large_html_name=`echo "$large_name" | sed "s,\.[jpgpnggif]*\$,.${GLOBAL_html_file_suffix},"` if [ "$large_name" = "$RETURN_large_html_name" ] then echo ERROR: I couldn\'t create an HTML name for "\"$large_name\""! >&2 exit 1 fi } source_name_to_half_html_name () { local half_name source_name_to_half_name "$*" half_name="$RETURN_half_name" RETURN_half_html_name=`echo "$half_name" | sed "s,\.[jpgpnggif]*\$,.${GLOBAL_html_file_suffix},"` if [ "$half_name" = "$RETURN_half_html_name" ] then echo ERROR: I couldn\'t create an HTML name for "\"$half_name\""! >&2 exit 1 fi } generated_name_to_source_name () { local is_generated_file nameroot source is_generated_name "$*" if [ $RETURN_is_generated_name -eq 1 ] then is_generated_file=1 nameroot=`echo "$*" | sed 's,-[bfhtrl]\.[jpgpnggif]*$,,'` else is_generated_file=0 nameroot=`echo "$*" | sed 's,\.[jpgpnggif]*$,,'` fi quote_egrep_chars "$nameroot" source=`egrep "^${RETURN_egrep}\.(jpg|gif|png)\$" $GLOBAL_image_list_tmpfile` if [ -z "$source" -o ! -f "$source" -o ! -s "$source" ] then echo ERROR: I couldn\'t find the source name from \"$*\"! >&2 exit 1 fi if [ "$*" = "$source" -a $is_generated_file -eq 1 ] then echo ERROR: I couldn\'t find convert source name with \"$*\"! >&2 exit 1 fi RETURN_source_name="$source" } quote_egrep_chars () { RETURN_egrep=`echo "$*" | sed -e 's,(,\\\\(,g' -e 's,),\\\\),g' -e 's,\+,\\\\+,g'` } quote_url_chars () { RETURN_url=`echo "$*" | sed -e 's, ,%20,g' -e 's,",%22,g'` } quote_html_chars () { RETURN_html=`echo "$*" | sed -e 's,&,\&,g' \ -e 's,<,\<,g' \ -e 's,>,\>,g' \ -e 's,",\",g'` } ######################################################### #### Image creation functions ######################################################### create_generated_files () { local fn local height width GLOBAL_there_is_at_least_one_reduced_img=0 GLOBAL_there_is_at_least_one_large_img=0 GLOBAL_there_is_at_least_one_half_img=0 progress_update_start_creating_reduced_images while read fn do get_dimensions "$fn" width="$RETURN_width"; height="$RETURN_height" create_thumbnail "$height" "$width" "$fn" create_feed "$height" "$width" "$fn" create_reduced "$height" "$width" "$fn" [ $RETURN_was_reduced -eq 1 ] && GLOBAL_there_is_at_least_one_reduced_img=1 create_large "$height" "$width" "$fn" [ $RETURN_was_larged -eq 1 ] && GLOBAL_there_is_at_least_one_large_img=1 create_half "$height" "$width" "$fn" [ $RETURN_was_halfed -eq 1 ] && GLOBAL_there_is_at_least_one_half_img=1 maybe_remove_original "$fn" done < $GLOBAL_image_list_tmpfile progress_update_done_creating_reduced_images } create_thumbnail () { local source_fn thumb_fn local source_height source_width source_height="$1" source_width="$2" source_fn="$3" source_name_to_thumb_name "$source_fn" thumb_fn="$RETURN_thumb_name" general_image_reducer \ $source_height $source_width \ 0 0 \ $GLOBAL_max_thumb_size $GLOBAL_max_thumb_size \ 1 \ "$source_fn" "$thumb_fn" if [ $RETURN_reduced_exists -eq 1 -a $RETURN_reduced_already_existed -eq 0 ] then progress_update_created_thumbnail_image fi } create_feed () { local source_fn thumb_fn local source_height source_width source_height="$1" source_width="$2" source_fn="$3" [ $GLOBAL_create_feed_images -eq 0 ] && return source_name_to_feed_name "$source_fn" feed_fn="$RETURN_feed_name" general_image_reducer \ $source_height $source_width \ 0 0 \ $GLOBAL_feed_height $GLOBAL_feed_width \ 1 \ "$source_fn" "$feed_fn" if [ $RETURN_reduced_exists -eq 1 -a $RETURN_reduced_already_existed -eq 0 ] then progress_update_created_feed_image fi } create_reduced () { local source_fn reduced_fn start_conv local source_height source_width source_height="$1" source_width="$2" shift; shift source_fn="$*" RETURN_was_reduced=0 [ $GLOBAL_reduce_big_pics -eq 0 ] && return source_name_to_reduced_name "$source_fn" reduced_fn="$RETURN_reduced_name" general_image_reducer \ $source_height $source_width \ $GLOBAL_reduce_trigger_height $GLOBAL_reduce_trigger_width \ $GLOBAL_reduce_height $GLOBAL_reduce_width \ 0 \ "$source_fn" "$reduced_fn" if [ $RETURN_reduced_exists -eq 1 ] then RETURN_was_reduced=1 if [ $RETURN_reduced_already_existed -eq 0 ] then progress_update_created_reduced_image fi fi } create_large () { local source_fn large_fn local source_height source_width source_height="$1" source_width="$2" shift; shift source_fn="$*" RETURN_was_larged=0 [ $GLOBAL_create_large_images -eq 0 ] && return source_name_to_large_name "$source_fn" large_fn="$RETURN_large_name" general_image_reducer \ $source_height $source_width \ $GLOBAL_large_trigger_height $GLOBAL_large_trigger_width \ $GLOBAL_large_height $GLOBAL_large_width \ 0 \ "$source_fn" "$large_fn" if [ $RETURN_reduced_exists -eq 1 ] then RETURN_was_larged=1 if [ $RETURN_reduced_already_existed -eq 0 ] then progress_update_created_large_image fi fi } create_half () { local source_fn large_fn local source_height source_width local dest_height dest_width source_height="$1" source_width="$2" shift; shift source_fn="$*" RETURN_was_halfed=0 [ $GLOBAL_create_half_images -eq 0 ] && return # Only create half-res versions for very large images -- larger than # 6 mpixels. Otherwise the "large" version is already good enough. [ `expr $source_height \* $source_width` -lt 6000000 ] && return source_name_to_half_name "$source_fn" half_fn="$RETURN_half_name" dest_height=`expr $source_height \* 7` dest_height=`expr $dest_height / 10` dest_width=`expr $source_width \* 7` dest_width=`expr $dest_width / 10` general_image_reducer \ $source_height $source_width \ 0 0 \ $dest_height $dest_width \ 0 \ "$source_fn" "$half_fn" if [ $RETURN_reduced_exists -eq 1 ] then RETURN_was_halfed=1 if [ $RETURN_reduced_already_existed -eq 0 ] then progress_update_created_half_image fi fi } general_image_reducer () { local source_height source_width local trigger_height trigger_width local dest_height dest_width local creating_a_thumbnail local source_name local target_name local start_conv local t local converted_with local width_delta height_delta local biggerdimen resizename source_height="$1" source_width="$2" trigger_height="$3" trigger_width="$4" dest_height="$5" dest_width="$6" creating_a_thumbnail="$7" source_name="$8" target_name="$9" RETURN_reduced_exists=0 RETURN_reduced_already_existed=0 if [ -f "$target_name" -a -s "$target_name" ] then [ $creating_a_thumbnail -eq 1 ] && get_dimensions "$target_name" width_delta=`expr $RETURN_width - $dest_width | tr -d '-'` height_delta=`expr $RETURN_height - $dest_height | tr -d '-'` if [ $creating_a_thumbnail -eq 1 -a \ $width_delta -ne 0 -a $width_delta -ne 1 -a \ $height_delta -ne 0 -a $height_delta -ne 1 ] then rm -f "$target_name" else make_file_world_readable "$target_name" RETURN_reduced_exists=1 RETURN_reduced_already_existed=1 return fi fi # If this is a portrait image, we need to swap the trigger/reduction dimensions. # FIXME This is controversial in my mind. The question is what a height/width # constraint means. Does it mean that's how much screen real estate you want # to use, or does it mean how many pixels you want in an image? If it's # referring to the screen real estate, then we want to reduce to height/width # regardless of whether the image is in portait or landscape orientation. # If it's referring to the # of pixels, then we want to swap height/width # when we're looking at a portrait photo vs a landscape photo. # The vast majority of programs on the net do the screen real estate path # because it's the obvious one. The following code errs on the side of pixel # count. I haven't made up my mind yet. # NB: A whole better way of doing this would be to specify scaling in terms # of the original size (e.g. "50%"), or specify the target size in terms of # megapixels. Maybe some day. if [ $source_height -gt $source_width ] then t=$trigger_height trigger_height=$trigger_width trigger_width=$t # t=$dest_height # dest_height=$dest_width # dest_width=$t fi if [ $creating_a_thumbnail -eq 1 -o \ $source_width -ge $trigger_width -o \ $source_height -ge $trigger_height ] then add_cleanup "$target_name" check_for_rotation "$source_name" if [ $GLOBAL_tools_to_use = sips ] then [ -z "$RETURN_rotation_angle" ] && RETURN_rotation_angle="0" # We scale to only a single side with sips (if we want to maintain the # correct aspect ratio), so select the larger of the two to make it # conform to the target dimension. if [ $source_height -gt $source_width ] then biggerdimen=$dest_height resizename=Height else biggerdimen=$dest_width resizename=Width fi sips --resample$resizename $biggerdimen \ --rotate $RETURN_rotation_angle \ --setProperty format jpeg \ --setProperty formatOptions $GLOBAL_compression_level \ $GLOBAL_sips_profile_convert_cmd1 \ $GLOBAL_sips_profile_convert_cmd2 \ "$GLOBAL_sips_profile_convert_cmd3" \ --addIcon \ "$source_name" --out "$target_name" 1>/dev/null 2>&1 | grep -v ^rejected converted_with="sips" elif [ $GLOBAL_tools_to_use = imagemagick ] then [ -z "$RETURN_rotation_angle" ] && RETURN_rotation_angle="0" convert -quality $GLOBAL_compression_level \ -rotate $RETURN_rotation_angle \ -geometry ${dest_width}x${dest_height} \ "$source_name" JPEG:- > "$target_name" converted_with="imagemagick" elif [ $GLOBAL_tools_to_use = netpbm ] then foo_to_pnm "$source_name" start_conv="$RETURN_foo_to_pnm" [ "$RETURN_rotation_angle" = "0" ] && RETURN_rotation_angle="" $start_conv "$source_name" | pnmscale -xysize $dest_width \ $dest_height 2>/dev/null | $RETURN_rotation_cmd $RETURN_rotation_angle | cjpeg -optimize -quality $GLOBAL_compression_level > "$target_name" converted_with="netpbm" fi if [ $GLOBAL_jhead_is_present -eq 1 -a $creating_a_thumbnail -eq 0 -a \ "x$converted_with" != xnetpbm ] then jhead -te "$source_name" "$target_name" >/dev/null 2>&1 fi make_file_world_readable "$target_name" remove_cleanup "$target_name" tag_image_with_text "$target_name" RETURN_reduced_exists=1 fi if [ $RETURN_reduced_exists -eq 1 -a ! -s "$target_name" ] then echo "ERROR: Unable to reduce \"$source_name\"! Exiting." >&2 exit 1 fi } reduced_file_exists () { local fn fn="$*" RETURN_reduced_exists=0 [ $GLOBAL_reduce_big_pics -eq 0 ] && return source_name_to_reduced_name="$fn" if [ -f "$RETURN_reduced_name" ] then RETURN_reduced_exists=1 fi } get_dimensions () { local fn conv dimens fn="$*" # Note that I redirect the output of djpeg to /dev/null. There # is something stupid about RH 8 where programs that get cut off # get a different signal than they did previously--both the yes(1) # invocation and djpeg emit errors on the RH8 system when they # never did in the past. I bet RH did something stupid. # I really don't want to send the output of djpeg or whatever to # /dev/null because its errors can often be useful to the user, but # I don't have much choice here (short of saving the error output to # a file and trying to grep out the bogus errors) if [ $GLOBAL_tools_to_use = sips ] then dimens="`sips -g pixelWidth "$fn" | tail -1 | sed 's,[^0-9]* *,,' | grep -v nil` `sips -g pixelHeight "$fn" | tail -1 | sed 's,[^0-9]* *,,' | grep -v nil`" elif [ $GLOBAL_tools_to_use = imagemagick ] then dimens=`identify -ping "$fn" 2>&1 | grep ' [0-9]*x[0-9]*[=+ ]' | sed 's,.* \([0-9]*\)x\([0-9]*\)[= +].*,\1 \2,' | head -1` elif [ $GLOBAL_tools_to_use = netpbm ] then foo_to_pnm "$fn" conv="$RETURN_foo_to_pnm" dimens=`$conv "$fn" 2>/dev/null | pnmfile | awk '{print $4 " " $6}'` fi RETURN_width=`echo $dimens | cut -d ' ' -f1` RETURN_height=`echo $dimens | cut -d ' ' -f2` if [ -z "$RETURN_width" -o -z "$RETURN_height" ] then echo ERROR: Internal error in get_dimensions, for some reason I couldn\'t get >&2 echo ERROR: the dimensions of \"$fn\". Exiting. >&2 exit 1 fi } check_for_rotation () { local fn rot_cmd fn="$*" RETURN_do_rotation=0 RETURN_rotation_cmd=cat RETURN_rotation_angle="0" [ ! -f $GLOBAL_rotation_filename ] && return if grep -i "^[a-z-]* ${fn}" $GLOBAL_rotation_filename >/dev/null 2>&1 then : else return fi rot_cmd=`grep -i "^[a-z-]* ${fn}" $GLOBAL_rotation_filename | sed "s/ ${fn}//"` if [ -z "$rot_cmd" ] then echo WARNING: Unrecognized rotation for "$fn"! Ignoring rotate. >&2 return fi if [ "$rot_cmd" != "clockwise" -a "$rot_cmd" != "counter-clockwise" ] then echo WARNING: Unrecognized rotation for "$fn"! Ignoring rotate. >&2 return fi RETURN_do_rotation=1 RETURN_rotation_cmd=pnmrotate if [ "$rot_cmd" = "clockwise" ] then RETURN_rotation_angle=-90 else RETURN_rotation_angle=90 fi } get_image_number () { local fn num total generated_name_to_source_name "$*" fn="$RETURN_source_name" num=`cat -n $GLOBAL_image_list_tmpfile | grep "^[ ]*[0-9]*[ ]*${fn}\$" | cut -f1 | sed 's,[^0-9],,g'` if [ -z "$num" ] then echo WARNING: Could not get image number for file \"$fn\"! 1>&2 fi RETURN_image_number="$num" total="$GLOBAL_total_image_count" if [ -z "$total" ] then echo WARNING: Could not determine number of image files! 1>&2 fi RETURN_number_of_images="$total" } tag_image_with_text () { local fn font_size font xoffset yoffset text fn="$*" [ -n "$GLOBAL_image_imprinting_text" ] || return [ "$GLOBAL_image_imprinting_text" = "undef" ] && return [ $GLOBAL_mogrify_is_present -eq 0 ] && return font_size=12 font="-*-helvetica-bold-r-*-*-${font_size}-*-*-*-*-*-iso8859-*" compute_subtitle_pixelwidth "$font_size" get_dimensions "$fn" xoffset=`expr $RETURN_width - $GLOBAL_image_subtitle_pixelwidth` [ $xoffset -le 0 ] && xoffset=0 yoffset=`expr $RETURN_height - $font_size` add_cleanup "$fn" text=`echo "$GLOBAL_image_imprinting_text" | sed "s,\",',g"` mogrify -fill yellow -font "$font" \ -draw "text ${xoffset},${yoffset} \"$text\"" "$fn" remove_cleanup "$fn" } # This function tries to determine the pixel width of a text string given # the string and given the font size that it'll be rendered in. I wish we # could just get the number directly from the imaging program that does it, # but I don't think I can. I need this information so I can justify the # text right-flush at the bottom of the image. # # The font is proportional, so these are just estimations. In short, # # upper-case char pixel width = font-size / 1.45 (8.27 pixels/char @ 12pts) # lower-case char pixel width = font-size / 2.20 (5.45 pixels/char @ 12pts) # space char pixel width = font-size / 2.22 (5.45 pixels/char @ 12pts) # all other chars pixel width = font-size / 1.60 (7.50 pixels/char @ 12pts) compute_subtitle_pixelwidth () { local font_size len len_padded lowcase upcase space otherchars pixellen font_size="$*" [ -n "$GLOBAL_image_subtitle_pixelwidth" ] && return len=`echo "$GLOBAL_image_imprinting_text" | wc -c | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` len_padded=`expr $len \* $font_size \* 10` lowcase=`echo "$GLOBAL_image_imprinting_text" | tr -cd '[a-z]' | wc -c | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` upcase=`echo "$GLOBAL_image_imprinting_text" | tr -cd '[A-Z0-9]' | wc -c | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` space=`echo "$GLOBAL_image_imprinting_text" | tr -cd '[ ]' | wc -c | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` otherchars=`expr $len - $upcase - $lowcase - $space` pixellen=`expr $lowcase \* $font_size \* 100 / 220` pixellen=`expr $pixellen + $upcase \* $font_size \* 100 / 145` pixellen=`expr $pixellen + $space \* $font_size \* 100 / 220` pixellen=`expr $pixellen + $otherchars \* $font_size \* 100 / 160` pixellen=`expr $pixellen + 2` GLOBAL_image_subtitle_pixelwidth="$pixellen" } ######################################################### #### HTML printing functions ######################################################### print_html_header () { get_directory_title echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' echo ' "http://www.w3.org/TR/html4/loose.dtd">' echo '<html>' echo '<head>' echo " <title>$RETURN_directory_title_inline</title>" [ -n "$GLOBAL_meta_tag" -a "$GLOBAL_meta_tag" != undef ] && echo " $GLOBAL_meta_tag" if [ "$GLOBAL_html_charset" != "undef" ] then echo " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=${GLOBAL_html_charset}\">" fi echo " <meta name=\"generator\" content=\"makethumbs v$GLOBAL_makethumbs_version\">" if [ -n "$GLOBAL_boilerplate_index_insert_in_head" -a \ "$GLOBAL_boilerplate_index_insert_in_head" != undef ] then echo "$GLOBAL_boilerplate_index_insert_in_head" elif [ -n "$GLOBAL_boilerplate_insert_in_head" -a \ "$GLOBAL_boilerplate_insert_in_head" != undef ] then echo "$GLOBAL_boilerplate_insert_in_head" fi echo '</head>' echo "$GLOBAL_body_tag" echo "" if [ -n "$GLOBAL_boilerplate_index_before_title" -a \ "$GLOBAL_boilerplate_index_before_title" != undef ] then echo "$GLOBAL_boilerplate_index_before_title" echo "" elif [ -n "$GLOBAL_boilerplate_before_title" -a \ "$GLOBAL_boilerplate_before_title" != undef ] then echo "$GLOBAL_boilerplate_before_title" echo "" fi if [ $GLOBAL_print_title_on_index -eq 1 ] then echo $ac_n "$GLOBAL_index_page_title_start_html$ac_c" echo $ac_n "$RETURN_directory_title$ac_c" echo "$GLOBAL_index_page_title_end_html" fi echo "" if [ -n "$GLOBAL_boilerplate_index_after_title" -a \ "$GLOBAL_boilerplate_index_after_title" != undef ] then echo "$GLOBAL_boilerplate_index_after_title" echo "" elif [ -n "$GLOBAL_boilerplate_after_title" -a \ "$GLOBAL_boilerplate_after_title" != undef ] then echo "$GLOBAL_boilerplate_after_title" echo "" fi get_directory_description if [ -n "$RETURN_directory_description" ] then echo "<p />$RETURN_directory_description" echo "<p />" echo "" fi } print_html_table_start () { case "$GLOBAL_index_table_spacing" in loose) echo '<table width="100%" cellspacing="10" cellpadding="0">' ;; tight) echo '<table width="100%" cellspacing="2" cellpadding="0">' ;; none | *) echo '<table width="100%" cellspacing="0" cellpadding="0">' ;; esac } print_html_table_end () { if [ $GLOBAL_last_row_was_closed -eq 0 ] then echo "</tr>" fi echo "" echo '</table>' if [ -n "$GLOBAL_boilerplate_index_after_table_before_indexlinks" -a \ "$GLOBAL_boilerplate_index_after_table_before_indexlinks" != "undef" ] then echo "$GLOBAL_boilerplate_index_after_table_before_indexlinks" fi } print_html_row_start () { echo "" echo '<tr>' } print_html_row_end () { echo '</tr>' echo "" if [ $GLOBAL_index_table_spacing = "loose" ] then # <!-- lame way to pad rows a bit but looks nice--> echo "<tr><td> </td></tr>" fi } print_html_column_start () { echo ' <td align="center">' } print_html_column_end () { echo ' </td>' } # FIXME: I can't decide if the user's footer text should come # before the "This page created" text or after that text. If they're # putting a <hr/> tag, it would probably look better to put the # This Page text after the user's footer text. print_html_footer () { local fn t date fn="$*" echo "" if [ -n "$GLOBAL_this_page_created_text" -a \ "$GLOBAL_this_page_created_text" != "undef" ] then date=`date '+%Y-%m-%d'` t=`echo "$GLOBAL_this_page_created_text" | sed -e 's,@LINKSTART@,<a href="http://www.molenda.com/makethumbs/">,' | sed -e 's,@LINKEND@,</a>,' -e "s,@DATE@,${date},"` [ -n "$t" ] && echo "<p>$t</p>" fi if [ -n "$GLOBAL_boilerplate_footer" -a "$GLOBAL_boilerplate_footer" != undef ] then echo "" echo "$GLOBAL_boilerplate_footer" echo "" fi cat << __EOM__ <!-- This file generated automatically by makethumbs.sh, written by Jason Molenda, makethumbs(AT)molenda.com. $GLOBAL_rcsid The latest version of this script is always available at http://www.molenda.com/makethumbs/ --> __EOM__ if [ -n "$GLOBAL_boilerplate_index_end_of_page" -a \ "$GLOBAL_boilerplate_index_end_of_page" != undef ] then echo "$GLOBAL_boilerplate_index_end_of_page" elif [ -n "$GLOBAL_boilerplate_end_of_page" -a \ "$GLOBAL_boilerplate_end_of_page" != undef ] then echo "$GLOBAL_boilerplate_end_of_page" else echo "$GLOBAL_body_end_tag" echo '</html>' fi add_checksum_to_file "$fn" remove_cleanup "$fn" } ######################################################### #### slideshow functions ######################################################### create_slideshow () { local fileset prev cur next [ $GLOBAL_create_slideshow -eq 0 ] && return progress_update_start_creating_slideshow_pages remove_existing_slideshow_html_files create_slideshow_image_list while read fileset do prev=`echo "$fileset" | cut -d\| -f1` cur=`echo "$fileset" | cut -d\| -f2` next=`echo "$fileset" | cut -d\| -f3` create_slideshow_source_file "$prev" "$cur" "$next" if [ $GLOBAL_reduce_big_pics -eq 1 -a \ $GLOBAL_there_is_at_least_one_reduced_img -eq 1 ] then create_slideshow_reduced_file "$prev" "$cur" "$next" fi if [ $GLOBAL_create_large_images -eq 1 -a \ $GLOBAL_there_is_at_least_one_large_img -eq 1 ] then create_slideshow_large_file "$prev" "$cur" "$next" fi if [ $GLOBAL_create_half_images -eq 1 -a \ $GLOBAL_there_is_at_least_one_half_img -eq 1 ] then create_slideshow_half_file "$prev" "$cur" "$next" fi progress_update_created_slideshow_page done < $GLOBAL_slideshow_image_list progress_update_done_creating_slideshow_pages } create_slideshow_source_file () { local prev_image cur_image next_image html_file prev_image="$1" cur_image="$2" next_image="$3" image_name_to_html_name "$cur_image" html_file="$RETURN_html_name" add_cleanup "$html_file" touch "$html_file" make_file_world_readable "$html_file" # The following exec goop so I don't have to manually redirect every # message to stderr in this function. exec 5>&1 # save stdout fd to fd #5 exec > "$html_file" # redirect stdout to the file print_slideshow_file_header "$prev_image" "$cur_image" "$next_image" print_slideshow_top_navlinks "$prev_image" "$cur_image" "$next_image" print_slideshow_image "$cur_image" "source" print_slideshow_size_links "source" "$cur_image" print_slideshow_image_description "$cur_image" print_slideshow_bottom_navlinks "$prev_image" "$cur_image" "$next_image" print_image_exif_info "$cur_image" print_slideshow_file_footer remove_cleanup "$html_file" exec 1>&5 # Copy stdout fd back from temporary save fd, #5 } create_slideshow_reduced_file () { local prev_image cur_image next_image local cur_source_image cur_reduced_image html_file prev_image="$1" cur_image="$2" next_image="$3" cur_source_image="$cur_image" cur_reduced_image="$cur_image" if [ "$prev_image" != NULL ] then source_name_to_reduced_name "$prev_image" prev_image="$RETURN_reduced_name" fi source_name_to_reduced_name "$cur_image" cur_reduced_image="$RETURN_reduced_name" if [ -f "$RETURN_reduced_name" ] then cur_image="$RETURN_reduced_name" else source_name_to_large_name "$cur_image" if [ -f "$RETURN_large_name" ] then cur_image="$RETURN_large_name" fi fi if [ "$next_image" != NULL ] then source_name_to_reduced_name "$next_image" next_image="$RETURN_reduced_name" fi image_name_to_html_name "$cur_reduced_image" html_file="$RETURN_html_name" add_cleanup "$html_file" touch "$html_file" make_file_world_readable "$html_file" # The following exec goop so I don't have to manually redirect every # message to stderr in this function. exec 5>&1 # save stdout fd to fd #5 exec > "$html_file" # redirect stdout to the file print_slideshow_file_header "$prev_image" "$cur_image" "$next_image" print_slideshow_top_navlinks "$prev_image" "$cur_image" "$next_image" print_slideshow_image "$cur_image" "reduced" print_slideshow_size_links "reduced" "$cur_image" print_slideshow_image_description "$cur_image" print_slideshow_bottom_navlinks "$prev_image" "$cur_image" "$next_image" print_image_exif_info "$cur_reduced_image" print_slideshow_file_footer remove_cleanup "$html_file" exec 1>&5 # Copy stdout fd back from temporary save fd, #5 } create_slideshow_large_file () { local prev_image cur_image next_image local cur_source_image cur_large_image html_file prev_image="$1" cur_image="$2" next_image="$3" cur_source_image="$cur_image" cur_large_image="$cur_image" if [ "$prev_image" != NULL ] then source_name_to_large_name "$prev_image" prev_image="$RETURN_large_name" fi source_name_to_large_name "$cur_image" cur_large_image="$RETURN_large_name" if [ -f "$RETURN_large_name" ] then cur_image="$RETURN_large_name" else source_name_to_reduced_name "$cur_image" if [ -f "$RETURN_reduced_name" ] then cur_image="$RETURN_reduced_name" fi fi if [ "$next_image" != NULL ] then source_name_to_large_name "$next_image" next_image="$RETURN_large_name" fi image_name_to_html_name "$cur_large_image" html_file="$RETURN_html_name" add_cleanup "$html_file" touch "$html_file" make_file_world_readable "$html_file" # The following exec goop so I don't have to manually redirect every # message to stderr in this function. exec 5>&1 # save stdout fd to fd #5 exec > "$html_file" # redirect stdout to the file print_slideshow_file_header "$prev_image" "$cur_image" "$next_image" print_slideshow_top_navlinks "$prev_image" "$cur_image" "$next_image" print_slideshow_image "$cur_image" "large" print_slideshow_size_links "large" "$cur_image" print_slideshow_image_description "$cur_image" print_slideshow_bottom_navlinks "$prev_image" "$cur_image" "$next_image" print_image_exif_info "$cur_large_image" print_slideshow_file_footer remove_cleanup "$html_file" exec 1>&5 # Copy stdout fd back from temporary save fd, #5 } create_slideshow_half_file () { local prev_image cur_image next_image local cur_source_image cur_half_image html_file prev_image="$1" cur_image="$2" next_image="$3" cur_source_image="$cur_image" cur_large_image="$cur_image" if [ "$prev_image" != NULL ] then source_name_to_half_name "$prev_image" prev_image="$RETURN_half_name" fi source_name_to_half_name "$cur_image" cur_half_image="$RETURN_half_name" if [ -f "$RETURN_half_name" ] then cur_image="$RETURN_half_name" else source_name_to_large_name "$cur_image" if [ -f "$RETURN_large_name" ] then cur_image="$RETURN_large_name" else source_name_to_reduced_name "$cur_image" if [ -f "$RETURN_reduce_name" ] then cur_image="$RETURN_reduce_name" fi fi fi if [ "$next_image" != NULL ] then source_name_to_half_name "$next_image" next_image="$RETURN_half_name" fi image_name_to_html_name "$cur_half_image" html_file="$RETURN_html_name" add_cleanup "$html_file" touch "$html_file" make_file_world_readable "$html_file" # The following exec goop so I don't have to manually redirect every # message to stderr in this function. exec 5>&1 # save stdout fd to fd #5 exec > "$html_file" # redirect stdout to the file print_slideshow_file_header "$prev_image" "$cur_image" "$next_image" print_slideshow_top_navlinks "$prev_image" "$cur_image" "$next_image" print_slideshow_image "$cur_image" "half" print_slideshow_size_links "half" "$cur_image" print_slideshow_image_description "$cur_image" print_slideshow_bottom_navlinks "$prev_image" "$cur_image" "$next_image" print_image_exif_info "$cur_large_image" print_slideshow_file_footer remove_cleanup "$html_file" exec 1>&5 # Copy stdout fd back from temporary save fd, #5 } javascript_navigate='<!-- The following Javascript lets people navigate with keyboard shortcuts --> <script type="text/javascript"><!-- document.onkeypress = handler; function handler (e) { key_n = 110; key_k = 107; // "n", "k" for next key_p = 112; key_j = 106; // "p", "j" for previous if (navigator.appName == "Netscape") { keyval = e.which; } else { keyval = window.event.keyCode; } @JAVASCRIPT_PREVIOUS@ @JAVASCRIPT_NEXT@ return; } //--></script>' javascript_next_img='if (keyval == key_n || keyval == key_k) { location = "@NEXT@"; return true; }' javascript_previous_img='if (keyval == key_p || keyval == key_j) { location = "@PREVIOUS@"; return true; }' print_slideshow_file_header () { local source_image prev_image next_image local t t_prev t_next prev_image="$1" source_image="$2" next_image="$3" get_image_caption "$source_image" is_this_a_boring_filename "$source_image" if [ $GLOBAL_slideshow_print_javascript_navigation -eq 1 ] then if [ "$prev_image" != NULL -a "$prev_image" ] then image_name_to_html_name "$prev_image" quote_url_chars "$RETURN_html_name" t_prev=`echo "$javascript_previous_img" | sed "s/@PREVIOUS@/${RETURN_url}/g"` fi if [ "$next_image" != NULL -a "$next_image" ] then image_name_to_html_name "$next_image" quote_url_chars "$RETURN_html_name" t_next=`echo "$javascript_next_img" | sed "s/@NEXT@/${RETURN_url}/g"` fi t=`echo "$javascript_navigate" | sed -e "s/@JAVASCRIPT_PREVIOUS@/${t_prev}/g" | sed -e "s/@JAVASCRIPT_NEXT@/${t_next}/g"` fi # echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' echo ' "http://www.w3.org/TR/html4/loose.dtd">' echo '<html>' echo '<head>' echo " <title>$RETURN_caption_inline</title>" [ -n "$GLOBAL_meta_tag" -a "$GLOBAL_meta_tag" != undef ] && echo " $GLOBAL_meta_tag" if [ "$GLOBAL_html_charset" != "undef" ] then echo " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=${GLOBAL_html_charset}\">" fi echo " <meta name=\"generator\" content=\"makethumbs v$GLOBAL_makethumbs_version\">" echo ' <!-- makethumbs generated HTML file which may be removed without warning -->' [ $GLOBAL_slideshow_print_javascript_navigation -eq 1 ] && echo "$t" if [ -n "$GLOBAL_boilerplate_slideshow_insert_in_head" -a \ "$GLOBAL_boilerplate_slideshow_insert_in_head" != undef ] then echo "$GLOBAL_boilerplate_slideshow_insert_in_head" elif [ -n "$GLOBAL_boilerplate_insert_in_head" -a \ "$GLOBAL_boilerplate_insert_in_head" != undef ] then echo "$GLOBAL_boilerplate_insert_in_head" fi echo '</head>' echo "$GLOBAL_body_tag" echo "" if [ -n "$GLOBAL_boilerplate_slideshow_before_title" -a \ "$GLOBAL_boilerplate_slideshow_before_title" != undef ] then echo "$GLOBAL_boilerplate_slideshow_before_title" echo "" elif [ -n "$GLOBAL_boilerplate_before_title" -a \ "$GLOBAL_boilerplate_before_title" != undef ] then echo "$GLOBAL_boilerplate_before_title" echo "" fi if [ $GLOBAL_print_title_on_slideshow -eq 1 ] then get_directory_title if [ -n "$RETURN_directory_title_inline" ] then if [ $RETURN_directory_title_is_from_user -eq 1 ] then echo $ac_n "$GLOBAL_slideshow_page_title_start_html$ac_c" if [ $GLOBAL_print_directory_title_on_slideshow_pages -eq 1 ] then echo $ac_n "$RETURN_directory_title_inline$ac_c" fi if [ $GLOBAL_heuristic_filenames_are_digicam_boring -eq 0 -o \ $RETURN_caption_was_from = "descriptions-file" -o \ $RETURN_is_boring -eq 0 ] then if [ $GLOBAL_print_directory_title_on_slideshow_pages -eq 1 ] then echo $ac_n " / $ac_c" fi echo $ac_n "$RETURN_caption_inline$ac_c" fi echo "$GLOBAL_slideshow_page_title_end_html" elif [ $RETURN_caption_was_from = "descriptions-file" -o \ $RETURN_is_boring -eq 0 ] then echo $ac_n "$GLOBAL_slideshow_page_title_start_html$ac_c" echo $ac_n "$RETURN_caption_inline$ac_c" echo "$GLOBAL_slideshow_page_title_end_html" fi fi fi if [ -n "$GLOBAL_boilerplate_slideshow_after_title" -a \ "$GLOBAL_boilerplate_slideshow_after_title" != undef ] then echo "$GLOBAL_boilerplate_slideshow_after_title" echo "" elif [ -n "$GLOBAL_boilerplate_after_title" -a \ "$GLOBAL_boilerplate_after_title" != undef ] then echo "$GLOBAL_boilerplate_after_title" echo "" fi } print_slideshow_size_links () { local current_image current_image_type local reduced_image large_image orig_image local reduced_link_text large_link_text orig_link_text half_link_text local size local spacer t current_image_type="$1" current_image="$2" if [ $GLOBAL_slideshow_img_size_across_two_lines -eq 1 ] then spacer="<br />" else spacer=" " fi generated_name_to_source_name "$current_image" orig_image="$RETURN_source_name" source_name_to_reduced_name "$orig_image" reduced_image="$RETURN_reduced_name" source_name_to_large_name "$orig_image" large_image="$RETURN_large_name" source_name_to_half_name "$orig_image" half_image="$RETURN_half_name" if [ -f "$reduced_image" ] then if [ $GLOBAL_print_img_size_on_slideshow -eq 1 ] then return_image_size "$reduced_image" size="${spacer}(${RETURN_image_size_str})" fi t=`echo "$GLOBAL_reduced_text" | sed -e 's, ,\ ,g'` if [ "$current_image_type" != "reduced" ] then image_name_to_html_name "$reduced_image" quote_url_chars "$RETURN_html_name" reduced_link_text="[<a href=\"$RETURN_url\">${t}</a>]$size" else reduced_link_text="[${t}]$size" fi fi if [ -f "$large_image" ] then if [ $GLOBAL_print_img_size_on_slideshow -eq 1 ] then return_image_size "$large_image" size="${spacer}(${RETURN_image_size_str})" fi t=`echo "$GLOBAL_large_text" | sed -e 's, ,\ ,g'` if [ "$current_image_type" != "large" ] then image_name_to_html_name "$large_image" quote_url_chars "$RETURN_html_name" large_link_text="[<a href=\"$RETURN_url\">${t}</a>]$size" else large_link_text="[${t}]$size" fi fi if [ -f "$half_image" ] then if [ $GLOBAL_print_img_size_on_slideshow -eq 1 ] then return_image_size "$half_image" size="${spacer}(${RETURN_image_size_str})" fi t=`echo "$GLOBAL_half_text" | sed -e 's, ,\ ,g'` if [ "$current_image_type" != "half" ] then image_name_to_html_name "$half_image" quote_url_chars "$RETURN_html_name" half_link_text="[<a href=\"$RETURN_url\">${t}</a>]$size" else half_link_text="[${t}]$size" fi fi if [ -f "$orig_image" ] then if [ $GLOBAL_print_img_size_on_slideshow -eq 1 ] then return_image_size "$orig_image" size="${spacer}(${RETURN_image_size_str})" fi t=`echo "$GLOBAL_original_text" | sed -e 's, ,\ ,g'` if [ "$current_image_type" != "source" ] then image_name_to_html_name "$orig_image" quote_url_chars "$RETURN_html_name" orig_link_text="[<a href=\"$RETURN_url\">${t}</a>]$size" else orig_link_text="[${t}]$size" fi fi [ -z "$reduced_link_text" -a -z "$large_link_text" -a -z "$half_link_text" ] && return echo '<div align="center">' echo ' <table cellspacing="0" cellpadding="0"><tr>' echo " <td align=\"center\">$reduced_link_text</td>" echo " <td> </td>" echo " <td align=\"center\">$large_link_text</td>" echo " <td> </td>" if [ -n "$half_link_text" ] then echo " <td align=\"center\">$half_link_text</td>" echo " <td> </td>" fi echo " <td align=\"center\">$orig_link_text</td>" echo ' </tr></table></div>' } print_slideshow_image_description () { local image_name image_name="$*" get_image_description "$image_name" if [ -n "$RETURN_image_description" ] then echo "<p /><div align=\"center\">$RETURN_image_description</div>" fi } print_slideshow_file_footer () { if [ -n "$GLOBAL_boilerplate_footer" -a \ "$GLOBAL_boilerplate_footer" != undef ] then echo "" echo "$GLOBAL_boilerplate_footer" echo "" fi if [ -n "$GLOBAL_boilerplate_slideshow_end_of_page" -a \ "$GLOBAL_boilerplate_slideshow_end_of_page" != undef ] then echo "$GLOBAL_boilerplate_slideshow_end_of_page" elif [ -n "$GLOBAL_boilerplate_end_of_page" -a \ "$GLOBAL_boilerplate_end_of_page" != undef ] then echo "$GLOBAL_boilerplate_end_of_page" else echo "$GLOBAL_body_end_tag" echo "</html>" fi } print_slideshow_image () { local cur_image type target_pre target_post local source_image click_target_image local caption_quoted cur_image="$1" type="$2" target_pre="" target_post="" get_image_caption "$cur_image" is_this_a_boring_filename "$cur_image" if [ $RETURN_is_boring -eq 1 -a $RETURN_caption_was_from = filename ] then caption_quoted="" else quote_html_chars "$RETURN_caption_inline" caption_quoted="$RETURN_html" fi # If we're going to make the image clickable, then clicking proceeds # in this order if all the image types exist: # reduced -> large -> source -> reduced -> large -> ... if [ $GLOBAL_slideshow_images_are_clickable -eq 1 ] then generated_name_to_source_name "$cur_image" source_image="$RETURN_source_name" source_name_to_large_name "$source_image" source_name_to_half_name "$source_image" source_name_to_reduced_name "$source_image" if [ "$type" = "reduced" ] then if [ -f "$RETURN_large_name" ] then click_target_image="$RETURN_large_name" else click_target_image="$source_image" fi fi if [ "$type" = "large" ] then if [ -f "$RETURN_half_name" ] then click_target_image="$RETURN_half_name" else click_target_image="$source_image" fi fi if [ "$type" = "half" ] then click_target_image="$source_image" fi if [ "$type" = "source" ] then if [ -f "$RETURN_reduced_name" ] then click_target_image="$RETURN_reduced_name" elif [ -f "$RETURN_large_name" ] then click_target_image="$RETURN_large_name" ] fi fi if [ -n "$click_target_image" -a "$click_target_image" != "$cur_image" ] then image_name_to_html_name "$click_target_image" quote_url_chars "$RETURN_html_name" target_pre="<a href=\"$RETURN_url\">" target_post="</a>" fi fi get_dimensions "$cur_image" echo "" quote_url_chars "$cur_image" echo "<div align=\"center\">$target_pre<img src=\"$RETURN_url\" height=\"$RETURN_height\" width=\"$RETURN_width\" alt=\"$caption_quoted\" />$target_post</div>" echo "" } print_slideshow_top_navlinks () { local prev_image cur_image next_image local prev_html cur_html next_html local prev_markup middle_markup next_markup index_href local imgno imgtotal right_text left_text local t prev_image="$1" cur_image="$2" next_image="$3" prev_html="$prev_image" cur_html="$cur_image" next_html="$next_image" middle_markup="" if [ "$prev_image" != NULL ] then image_name_to_html_name "$prev_image" prev_html="$RETURN_html_name" fi image_name_to_html_name "$cur_image" cur_html="$RETURN_html_name" if [ "$next_image" != NULL ] then image_name_to_html_name "$next_image" next_html="$RETURN_html_name" fi ## Emit link to previous image if [ "$prev_image" = NULL ] then prev_markup="$GLOBAL_slideshow_previous_pre_link$GLOBAL_slideshow_previous$GLOBAL_slideshow_previous_post_link" else quote_url_chars "$prev_html" prev_markup="$GLOBAL_slideshow_previous_pre_link<a href=\"$RETURN_url\">$GLOBAL_slideshow_previous</a>$GLOBAL_slideshow_previous_post_link" fi ## Emit link to next image if [ "$next_image" = NULL ] then next_markup="$GLOBAL_slideshow_next_pre_link$GLOBAL_slideshow_next$GLOBAL_slideshow_next_post_link" else quote_url_chars "$next_html" next_markup="$GLOBAL_slideshow_next_pre_link<a href=\"$RETURN_url\">$GLOBAL_slideshow_next</a>$GLOBAL_slideshow_next_post_link" fi ## Emit link to index map_image_to_index "$cur_image" middle_markup="$GLOBAL_slideshow_ret_to_index_pre_link<a href=\"${RETURN_index_name}\">$GLOBAL_slideshow_ret_to_index</a>$GLOBAL_slideshow_ret_to_index_post_link" ## Get "Image xx of yy" info get_image_number "$cur_image" imgno="$RETURN_image_number" imgtotal="$RETURN_number_of_images" if [ -n "$imgno" -a -n "$imgtotal" ] then t=`echo "$GLOBAL_image_xx_of_yy_text" | sed -e "s,@CURRENT@,${imgno}," -e "s,@TOTAL@,${imgtotal},"` right_txt="$t" left_txt="" else right_txt=" " left_txt=" " fi get_image_timestamp_from_dates_file "$cur_image" if [ -n "$RETURN_timestamp" ] then left_txt="$RETURN_timestamp" fi echo '' echo '<table width="100%">' echo "<tr>" echo " <td align=\"left\" width=\"25%\">$left_txt</td>" echo ' <td align="center" width="50%">' echo ' <table><tr>' echo " <td>$prev_markup</td>" echo " <td> </td>" echo " <td>$middle_markup</td>" echo " <td> </td>" echo " <td>$next_markup</td>" echo ' </tr></table>' echo ' </td>' echo " <td align=\"right\" width=\"25%\">$right_txt</td>" echo '</tr>' echo '</table>' echo '' } print_slideshow_bottom_navlinks () { local prev_image cur_image next_image local prev_html cur_html next_html local prev_markup middle_markup next_markup index_href local imgno imgtotal right_text left_text local t [ $GLOBAL_slideshow_print_bottom_navlinks -eq 0 ] && return prev_image="$1" cur_image="$2" next_image="$3" prev_html="$prev_image" cur_html="$cur_image" next_html="$next_image" middle_markup="" if [ "$prev_image" != NULL ] then image_name_to_html_name "$prev_image" prev_html="$RETURN_html_name" fi image_name_to_html_name "$cur_image" cur_html="$RETURN_html_name" if [ "$next_image" != NULL ] then image_name_to_html_name "$next_image" next_html="$RETURN_html_name" fi ## Emit link to previous image if [ "$prev_image" = NULL ] then prev_markup="$GLOBAL_slideshow_previous_pre_link$GLOBAL_slideshow_previous$GLOBAL_slideshow_previous_post_link" else quote_url_chars "$prev_html" prev_markup="$GLOBAL_slideshow_previous_pre_link<a href=\"$RETURN_url\">$GLOBAL_slideshow_previous</a>$GLOBAL_slideshow_previous_post_link" fi ## Emit link to next image if [ "$next_image" = NULL ] then next_markup="$GLOBAL_slideshow_next_pre_link$GLOBAL_slideshow_next$GLOBAL_slideshow_next_post_link" else quote_url_chars "$next_html" next_markup="$GLOBAL_slideshow_next_pre_link<a href=\"$RETURN_url\">$GLOBAL_slideshow_next</a>$GLOBAL_slideshow_next_post_link" fi ## Emit link to index map_image_to_index "$cur_image" middle_markup="$GLOBAL_slideshow_ret_to_index_pre_link<a href=\"${RETURN_index_name}\">$GLOBAL_slideshow_ret_to_index</a>$GLOBAL_slideshow_ret_to_index_post_link" ## Get "Image xx of yy" info right_txt=" " left_txt=" " echo '' echo '<table width="100%">' echo "<tr>" echo " <td align=\"left\" width=\"25%\">$left_txt</td>" echo ' <td align="center" width="50%">' echo ' <table><tr>' echo " <td>$prev_markup</td>" echo " <td> </td>" echo " <td>$middle_markup</td>" echo " <td> </td>" echo " <td>$next_markup</td>" echo ' </tr></table>' echo ' </td>' echo " <td align=\"right\" width=\"25%\">$right_txt</td>" echo '</tr>' echo '</table>' echo '' } create_slideshow_image_list () { local number_of_files local prev_num cur_num next_num local prev_image cur_image next_image number_of_files=`cat $GLOBAL_image_list_tmpfile | wc -l | sed 's, ,,g'` cur_num=1 make_tmpfile ss-source GLOBAL_slideshow_image_list=$RETURN_tmpfile while [ $cur_num -le $number_of_files ] do prev_num=`expr $cur_num - 1` next_num=`expr $cur_num + 1` if [ $cur_num -eq 1 ] then prev_image=NULL else prev_image=`cat $GLOBAL_image_list_tmpfile | sed -n "${prev_num}p"` fi cur_image=`cat $GLOBAL_image_list_tmpfile | sed -n "${cur_num}p"` if [ $cur_num -eq $number_of_files ] then next_image=NULL else next_image=`cat $GLOBAL_image_list_tmpfile | sed -n "${next_num}p"` fi echo "${prev_image}|${cur_image}|${next_image}" >> $GLOBAL_slideshow_image_list cur_num=`expr $cur_num + 1` done } remove_existing_slideshow_html_files () { local file_list fn make_tmpfile file_list file_list=$RETURN_tmpfile ls -1 | grep "\.$GLOBAL_html_file_suffix" > $file_list while read fn do if grep "makethumbs generated HTML file which may be removed without warning" "$fn" >/dev/null 2>&1 then rm -f "$fn" fi done < $file_list } ######################################################### #### Progress printing functions ######################################################### # Descriptions.txt progress_update_start_descriptions () { [ $GLOBAL_show_progress -eq 0 ] && return if [ $GLOBAL_show_timings -eq 1 ] then GLOBAL_progress_timestamp_start=`date '+%s' 2>/dev/null` fi } progress_update_created_descriptions () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "Created $GLOBAL_descriptions_filename$ac_c" >&2 if [ $GLOBAL_show_timings -eq 1 ] then print_timestamp_diff $GLOBAL_progress_timestamp_start `date '+%s'` fi echo "" >&2 } progress_update_updated_descriptions () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "Updated $GLOBAL_descriptions_filename$ac_c" >&2 if [ $GLOBAL_show_timings -eq 1 ] then print_timestamp_diff $GLOBAL_progress_timestamp_start `date '+%s'` fi echo "" >&2 } # Dates.txt progress_update_start_updating_dates () { [ $GLOBAL_show_progress -eq 0 ] && return if [ $GLOBAL_show_timings -eq 1 ] then GLOBAL_progress_timestamp_start=`date '+%s' 2>/dev/null` fi } progress_update_done_updating_dates () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "Updated $GLOBAL_dates_filename$ac_c" >&2 if [ $GLOBAL_show_timings -eq 1 ] then print_timestamp_diff $GLOBAL_progress_timestamp_start `date '+%s'` fi echo "" >&2 } progress_update_start_creating_dates () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "Creating $GLOBAL_dates_filename $ac_c" >&2 if [ $GLOBAL_show_timings -eq 1 ] then GLOBAL_progress_timestamp_start=`date '+%s' 2>/dev/null` fi } progress_update_created_date () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n ".$ac_c" >&2 } progress_update_done_creating_dates () { local time [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n " done.$ac_c" >&2 if [ $GLOBAL_show_timings -eq 1 ] then print_timestamp_diff $GLOBAL_progress_timestamp_start `date '+%s'` fi echo "" >&2 } # index.html progress_update_start_creating_index_html () { local fn fn="$*" [ -z "$fn" ] && fn="$GLOBAL_index_filename" if [ $GLOBAL_show_timings -eq 1 ] then GLOBAL_progress_timestamp_start=`date '+%s' 2>/dev/null` fi [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "Creating $fn$ac_c" >&2 } progress_update_done_creating_index_html () { local time [ $GLOBAL_show_progress -eq 0 ] && return if [ $GLOBAL_show_timings -eq 1 ] then print_timestamp_diff $GLOBAL_progress_timestamp_start `date '+%s'` fi echo "" >&2 } # reduced/thumbnail images progress_update_start_creating_reduced_images () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "Making reduced/thumbnail images $ac_c" >&2 GLOBAL_did_we_reduce_anything=0 if [ $GLOBAL_show_timings -eq 1 ] then GLOBAL_progress_timestamp_start=`date '+%s' 2>/dev/null` fi } progress_update_created_feed_image () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n ",$ac_c" >&2 GLOBAL_did_we_reduce_anything=1 } progress_update_created_reduced_image () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "o$ac_c" >&2 GLOBAL_did_we_reduce_anything=1 } progress_update_created_large_image () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "O$ac_c" >&2 GLOBAL_did_we_reduce_anything=1 } progress_update_created_half_image () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "0$ac_c" >&2 GLOBAL_did_we_reduce_anything=1 } progress_update_created_thumbnail_image () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n ".$ac_c" >&2 GLOBAL_did_we_reduce_anything=1 } progress_update_done_creating_reduced_images () { [ $GLOBAL_show_progress -eq 0 ] && return if [ $GLOBAL_did_we_reduce_anything -eq 0 ] then echo $ac_n "(none regenerated)$ac_c" >&2 else echo $ac_n " done.$ac_c" >&2 fi if [ $GLOBAL_show_timings -eq 1 ] then print_timestamp_diff $GLOBAL_progress_timestamp_start `date '+%s'` fi echo "" >&2 } # slideshow pages progress_update_start_creating_slideshow_pages () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n "Making slideshow pages $ac_c" >&2 if [ $GLOBAL_show_timings -eq 1 ] then GLOBAL_progress_timestamp_start=`date '+%s' 2>/dev/null` fi } progress_update_created_slideshow_page () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n ".$ac_c" >&2 } progress_update_done_creating_slideshow_pages () { [ $GLOBAL_show_progress -eq 0 ] && return echo $ac_n " done.$ac_c" >&2 if [ $GLOBAL_show_timings -eq 1 ] then print_timestamp_diff $GLOBAL_progress_timestamp_start `date '+%s'` fi echo "" >&2 } print_timestamp_diff () { local start end local secs local mins start=$1 end=$2 secs="" mins="" [ -z "$start" -o -z "$end" ] && return secs=`expr $end - $start 2>/dev/null` [ -z "$secs" ] && return if [ $secs -ge 100 ] then mins=`expr $secs / 60` secs=`expr $secs % 60` echo $ac_n " ($mins minutes $secs seconds)$ac_c" >&2 else echo $ac_n " ($secs seconds)$ac_c" >&2 fi } ######################################################### #### checksum checking functions, unmodified file removal ######################################################### remove_file_if_unmodified () { local filename filename="$*" verify_file_checksum "$filename" if [ $RETURN_cksum_ok -eq 1 ] then rm -f "$filename" return fi } # $RETURN_cksum_ok is '1' if a valid checksum is found. If no checksum # is found, or the checksum doesn't match, a '0' is returned (i.e. the # user may have modified this file for all we know). verify_file_checksum () { local filename sum_program recorded_sum sum_output filename="$*" RETURN_cksum_ok=0 if grep '<!-- makethumbs checksum ' "$filename" >/dev/null 2>&1 then : else return fi sum_program=`grep '<!-- makethumbs checksum' "$filename" | tail -1 | sed -e 's,.*makethumbs checksum \([^ ]*\) \(.*\) -->,\1,'` recorded_sum=`grep '<!-- makethumbs checksum' "$filename" | tail -1 | sed -e 's,.*makethumbs checksum \([^ ]*\) \(.*\) -->,\2,'` find_in_path "$sum_program" [ $RETURN_found -eq 0 ] && return sum_output=`grep -v '<!-- makethumbs checksum' "$filename" | "$sum_program"` if [ "$sum_program" = md5sum -o "$sum_program" = sha1sum ] then sum_output=`echo "$sum_output" | awk '{print $1}'` fi normalize_checksum_string "$sum_output" sum_output="$RETURN_str_norm" normalize_checksum_string "$recorded_sum" recorded_sum="$RETURN_str_norm" if [ -n "$sum_output" -a -n "$recorded_sum" -a \ "$sum_output" = "$recorded_sum" ] then RETURN_cksum_ok=1 fi } find_checksum_program () { local i for i in md5sum md5 sha1sum cksum sum do find_in_path $i if [ $RETURN_found -eq 1 ] then RETURN_cksum_prog=$i return fi done RETURN_cksum_prog="" } add_checksum_to_file () { local filename sum filename="$*" find_checksum_program [ -z "$RETURN_cksum_prog" ] && return [ -z "$filename" ] && return if grep '<!-- makethumbs checksum ' "$filename" >/dev/null 2>&1 then return fi sum=`cat $filename | $RETURN_cksum_prog` if [ $RETURN_cksum_prog = md5sum -o $RETURN_cksum_prog = sha1sum ] then sum=`echo "$sum" | awk '{print $1}'` fi normalize_checksum_string "$sum" sum="$RETURN_str_norm" echo "<!-- makethumbs checksum $RETURN_cksum_prog $sum -->" >> "$filename" } normalize_checksum_string () { local istr istr="$*" istr=`echo "$istr" | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,' \ -e 's,[ ][ ]*, ,'` RETURN_str_norm="$istr" } ######################################################### #### descriptions.txt support ######################################################### create_descriptions_file () { local max i l progress_update_start_descriptions if [ -f $GLOBAL_descriptions_filename ] then update_descriptions_file return fi add_cleanup $GLOBAL_descriptions_filename touch $GLOBAL_descriptions_filename make_file_world_readable $GLOBAL_descriptions_filename echo '[short title] (best to avoid HTML markup here, keep it short, <br>s are OK)' > $GLOBAL_descriptions_filename echo '' >> $GLOBAL_descriptions_filename echo '' >> $GLOBAL_descriptions_filename echo '[longer page description] (all the HTML you want, as many lines as you want)' >> $GLOBAL_descriptions_filename echo '' >> $GLOBAL_descriptions_filename echo '' >> $GLOBAL_descriptions_filename echo '[captions] (best to avoid HTML markup here, keep it short, <br>s are OK)' >> $GLOBAL_descriptions_filename echo '' >> $GLOBAL_descriptions_filename # find max length of image names max=0 while read i do l=`echo "$i" | awk '{print length}'` [ $l -gt $max ] && max=$l done < $GLOBAL_image_list_tmpfile while read i do echo "$i" | awk "{printf (\"%-${max}s \\n\""', $0);}' >> $GLOBAL_descriptions_filename done < $GLOBAL_image_list_tmpfile echo '' >> $GLOBAL_descriptions_filename echo '[descriptions] (all the HTML you want, but each file on just one line)' >> $GLOBAL_descriptions_filename echo '' >> $GLOBAL_descriptions_filename while read i do echo "$i" | awk "{printf (\"%-${max}s \\n\""', $0);}' >> $GLOBAL_descriptions_filename done < $GLOBAL_image_list_tmpfile remove_cleanup $GLOBAL_descriptions_filename make_file_world_readable $GLOBAL_descriptions_filename progress_update_created_descriptions } update_descriptions_file () { local filenames_to_add fn new_desc oldsz newsz make_tmpfile newfiles filenames_to_add="$RETURN_tmpfile" while read fn do check_if_file_in_descriptions_file "$fn" if [ $RETURN_file_is_present -eq 0 ] then echo "$fn " >> $filenames_to_add fi done < $GLOBAL_image_list_tmpfile [ ! -s $filenames_to_add ] && return make_tmpfile new-desc new_desc="$RETURN_tmpfile" cat $GLOBAL_descriptions_filename | sed '/^\[captions\]/,$d' > $new_desc grep '^\[captions\]' $GLOBAL_descriptions_filename >> $new_desc echo "" >> $new_desc cat $GLOBAL_descriptions_filename | sed '1,/^\[captions\]/d' | sed '/^\[descriptions\]/,$d' | grep -v '^[ ]*$' >> $new_desc cat $filenames_to_add >> $new_desc echo "" >> $new_desc grep '^\[descriptions\]' $GLOBAL_descriptions_filename >> $new_desc echo "" >> $new_desc cat $GLOBAL_descriptions_filename | sed '1,/^\[descriptions\]/d' | grep -v '^[ ]*$' >> $new_desc cat $filenames_to_add >> $new_desc [ ! -s "$new_desc" ] && return oldsz=`ls -l $GLOBAL_descriptions_filename | awk '{print $5}' | sed 's,[^0-9],,g'` newsz=`ls -l $new_desc | awk '{print $5}' | sed 's,[^0-9],,g'` # If the new one isn't larger than the old one, something is wrong. if [ $newsz -gt $oldsz ] then cat $new_desc > $GLOBAL_descriptions_filename else echo WARNING: Some problem updating the $GLOBAL_descriptions_filename 1>&2 fi progress_update_updated_descriptions } # validate_descriptions_file () { local is_valid [ ! -f $GLOBAL_descriptions_filename ] && return [ $GLOBAL_descriptions_file_validated -eq 1 ] && return [ $GLOBAL_descriptions_file_is_invalid -eq 1 ] && return is_valid=`cat $GLOBAL_descriptions_filename | egrep '^\[short title\]|^\[longer page description\]|^\[captions\]|^\[descriptions\]' | awk 'BEGIN { state = 0 } { \ if ($1 == "[short" && $2 == "title]" && state == 0 ) { state = 1 ; next } if ($1 == "[longer" && $2 == "page" && state == 1) { state = 2 ; next} if ($1 == "[captions]" && state == 2) { state = 3; next} if ($1 == "[descriptions]" && state == 3) { print "ok"; next} print "error" }'` if [ -n "$is_valid" ] then if [ "$is_valid" = "ok" ] then GLOBAL_descriptions_file_validated=1 else echo WARNING: Invalid $GLOBAL_descriptions_filename ! Ignoring. 1>&2 GLOBAL_descriptions_file_is_invalid=1 fi fi } check_if_file_in_descriptions_file () { local title_count no_title_count tot RETURN_file_is_present=0 quote_egrep_chars "$*" title_count=`egrep "^$RETURN_egrep[ ]*[^ ].*\$" $GLOBAL_descriptions_filename | wc -l | sed 's,[^0-9],,g'` no_title_count=`egrep "^$RETURN_egrep[ ]*\$" $GLOBAL_descriptions_filename | wc -l | sed 's,[^0-9],,g'` tot=`expr "$title_count" + "$no_title_count"` if [ -n "$tot" -a "$tot" -ge 2 ] then RETURN_file_is_present=1 fi if [ $GLOBAL_no_image_titles_or_descriptions -eq 1 -a "$title_count" -gt 0 ] then GLOBAL_no_image_titles_or_descriptions=0 fi } get_short_page_title_from_descriptions_file () { RETURN_short_page_title="" [ ! -f $GLOBAL_descriptions_filename ] && return validate_descriptions_file [ $GLOBAL_descriptions_file_validated -eq 0 ] && return RETURN_short_page_title=`cat $GLOBAL_descriptions_filename | grep -v '^#' | awk 'BEGIN { seen_title = 0; seen_longerdesc = 0; } \ { \ if ($1 == "[short" && $2 == "title]") { seen_title = 1; next; } if (seen_title != 1) { next; } if ($1 == "[longer" && $2 == "page") { seen_longerdesc = 1; next; } if (seen_longerdesc == 1) { next; } print; }' | grep -v '^[ ]*$'` RETURN_short_page_title=`echo "$RETURN_short_page_title" | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` } get_page_description_from_descriptions_file () { RETURN_page_description="" [ ! -f $GLOBAL_descriptions_filename ] && return validate_descriptions_file [ $GLOBAL_descriptions_file_validated -eq 0 ] && return RETURN_page_description=`cat $GLOBAL_descriptions_filename | grep -v '^#' | awk 'BEGIN { seen_longerdesc = 0; seen_captions = 0; } \ { \ if ($1 == "[longer" && $2 == "page") { seen_longdesc = 1; next; } if (seen_longdesc != 1) { next; } if ($1 == "[captions]") { seen_captions = 1; next; } if (seen_captions == 1) { next; } print; }' | grep -v '^[ ]*$'` RETURN_page_description=`echo "$RETURN_page_description" | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` } get_image_caption_from_descriptions_file () { local fn fn="$*" RETURN_image_caption="" [ ! -f $GLOBAL_descriptions_filename ] && return [ $GLOBAL_no_image_titles_or_descriptions -eq 1 ] && return [ -z "$fn" ] && return validate_descriptions_file [ $GLOBAL_descriptions_file_validated -eq 0 ] && return generated_name_to_source_name "$fn" fn="$RETURN_source_name" RETURN_image_caption=`\ egrep "^${fn}"'[ ]|^\[captions\]|^\[descriptions\]' \ $GLOBAL_descriptions_filename | awk 'BEGIN { seen_captions = 0; seen_desc = 0; } \ { \ if ($1 == "[captions]") { seen_captions = 1; next; } if (seen_captions != 1) { next; } if ($1 == "[descriptions]") { seen_desc = 1; next; } if (seen_desc == 1) { next; } print }' | grep -v '^[ ]*$' | head -1` RETURN_image_caption=`echo "$RETURN_image_caption" | sed "s/${fn}//" | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` } get_image_description_from_descriptions_file () { local fn fn="$*" RETURN_image_description="" [ ! -f $GLOBAL_descriptions_filename ] && return [ $GLOBAL_no_image_titles_or_descriptions -eq 1 ] && return [ -z "$fn" ] && return validate_descriptions_file [ $GLOBAL_descriptions_file_validated -eq 0 ] && return generated_name_to_source_name "$fn" fn="$RETURN_source_name" RETURN_image_description=`\ egrep "^${fn}"'[ ]|^\[captions\]|^\[descriptions\]' \ $GLOBAL_descriptions_filename | awk 'BEGIN { seen_desc = 0; } \ { \ if ($1 == "[descriptions]") { seen_desc = 1; next; } if (seen_desc != 1) { next; } print }' | grep -v '^[ ]*$' | head -1` RETURN_image_description=`echo "$RETURN_image_description" | sed "s/${fn}//" | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` } # Return the filenames in the [captions] section, in the order they # appear in the file. get_caption_filenames_from_descriptions_file () { RETURN_captions_tmpfile="" [ ! -f $GLOBAL_descriptions_filename ] && return validate_descriptions_file [ $GLOBAL_descriptions_file_validated -eq 0 ] && return make_tmpfile captions RETURN_captions_tmpfile="$RETURN_tmpfile" cat $GLOBAL_descriptions_filename | sed '1,/^\[captions\]/d' | sed '/^\[descriptions\]/,$d' | grep -v '^[ ]*$' | sed 's,^\(.*\)\.\([jgp][pin][gfg]\)[ ].*,\1.\2,' \ >> $RETURN_captions_tmpfile } ## The functions that the rest of the script will use: get_image_caption () { local fn fn="$*" get_image_caption_from_descriptions_file "$fn" if [ -n "$RETURN_image_caption" ] then RETURN_caption="$RETURN_image_caption" RETURN_caption_was_from="descriptions-file" RETURN_caption_inline=`echo "$RETURN_caption" | sed -e 's, *<[Bb][Rr]> *, ,g' -e 's, *<[Bb][Rr] */> *, ,g'` else RETURN_caption=`echo "$fn" | sed -e 's,-[bfhtrl]\.[jpgpnggif]*$,,' \ -e 's,\.[jpgpnggif]*$,,' \ -e 's,-, ,g' -e 's,_, ,g'` RETURN_caption_inline="$RETURN_caption" RETURN_caption_was_from="filename" fi } get_image_caption_for_main_index () { local fn fn="$*" get_image_caption "$fn" [ "$RETURN_caption_was_from" = "descriptions-file" ] && return [ $GLOBAL_use_timestamps_as_captions -eq 0 ] && return if [ $GLOBAL_heuristic_filenames_are_digicam_boring -eq 1 ] then get_image_timestamp_from_dates_file "$fn" if [ $GLOBAL_heuristic_days_are_identical -eq 0 -a -n "$RETURN_date" ] then iso8601_format_datestr_to_human "$RETURN_date" if [ -n "$RETURN_human_date" ] then RETURN_caption="$RETURN_human_date" return fi fi if [ -n "$RETURN_time" ] then RETURN_caption="$RETURN_time" return fi fi } is_this_a_boring_filename () { local fn t generated_name_to_source_name "$*" fn="$RETURN_source_name" RETURN_is_boring=0 [ $GLOBAL_heuristic_filenames_are_digicam_boring -eq 0 ] && return t=`echo "$fn" | sed 's,[ ]*[0-9]*[ ]*,,g'` if [ -n "$t" -a -n "$GLOBAL_heuristic_filename_pattern" -a \ "$t" = "$GLOBAL_heuristic_filename_pattern" ] then RETURN_is_boring=1 fi } get_image_description () { get_image_description_from_descriptions_file "$*" } get_directory_title () { local t get_short_page_title_from_descriptions_file if [ -n "$RETURN_short_page_title" ] then RETURN_directory_title="$RETURN_short_page_title" RETURN_directory_title_inline=`echo "$RETURN_short_page_title" | sed -e 's, *<[Bb][Rr]> *, ,g' -e 's, *<[Bb][Rr] */> *, ,g'` RETURN_directory_title_is_from_user=1 else t=`pwd` RETURN_directory_title=`basename "$t"` RETURN_directory_title_inline="$RETURN_directory_title" RETURN_directory_title_is_from_user=0 fi } get_directory_description () { get_page_description_from_descriptions_file RETURN_directory_description="$RETURN_page_description" } ######################################################### #### dates.txt support ######################################################### create_dates_file () { local fn if [ -f $GLOBAL_dates_filename ] then update_dates_file return fi touch $GLOBAL_dates_filename make_file_world_readable $GLOBAL_dates_filename cat > $GLOBAL_dates_filename << __EOF__ # This is a one-time generated file (but updated whenever new images are added) # which records the timestamps of images for various purposes. You can modify # any entry if you wish--makethumbs will not regenerate a timestamp unless # this file is completely removed or an image's entry in this file is removed. # Each line has three fields -- FILENAME | DATE | TIME . # DATE should be YYYY-MM-DD, but need not be. # TIME should be HH:MM, but need not be. __EOF__ add_cleanup $GLOBAL_dates_filename progress_update_start_creating_dates while read fn do get_image_time_date "$fn" echo "${fn}|${RETURN_date}|${RETURN_time}" >> $GLOBAL_dates_filename progress_update_created_date done < $GLOBAL_image_list_tmpfile remove_cleanup "$GLOBAL_dates_filename" make_file_world_readable "$GLOBAL_dates_filename" progress_update_done_creating_dates } update_dates_file () { local fn updated [ ! -f $GLOBAL_dates_filename ] && return updated=0 progress_update_start_updating_dates while read fn do if grep "^${fn}|.*|.*" $GLOBAL_dates_filename >/dev/null 2>&1 then continue fi get_image_time_date "$fn" echo "${fn}|${RETURN_date}|${RETURN_time}" >> $GLOBAL_dates_filename updated=1 done < $GLOBAL_image_list_tmpfile [ $updated -eq 1 ] && progress_update_done_updating_dates } get_image_timestamp_from_dates_file () { local fn l RETURN_timestamp="" RETURN_date="" RETURN_time="" generated_name_to_source_name "$*" fn="$RETURN_source_name" [ ! -f $GLOBAL_dates_filename ] && return l=`grep "^${fn}|.*|.*$" $GLOBAL_dates_filename 2>/dev/null | head -1` if [ -n "$l" ] then if [ `echo "$l" | awk -F\| '{print NF}'` -eq 3 ] then RETURN_date=`echo "$l" | awk -F\| '{print $2}' | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` RETURN_time=`echo "$l" | awk -F\| '{print $3}'| sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` if [ -n "$RETURN_time" -a -n "$RETURN_date" ] then RETURN_timestamp="$RETURN_date $RETURN_time" else RETURN_timestamp="$RETURN_date$RETURN_time" fi fi fi } # Set up some global variables for the index.html image captioning. # If the user hasn't set a caption by hand, and the filename looks # uninteresting (e.g. it's a digicam name like P8382010.jpg), then # # if we have multiple days in this directory, let's use the day name # for the caption. # # if all images are from the same day, then let's use the time. # # This function sets up the necessary state to do something intelligent # at caption emitting time. analyze_filenames_and_dates () { local t empty_count same_day_count count boring_pattern GLOBAL_total_image_count=`cat "$GLOBAL_image_list_tmpfile" | wc -l | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` [ $GLOBAL_total_image_count -eq 0 ] && return t=`cat "$GLOBAL_image_list_tmpfile" | grep '[0-9][0-9][0-9]' | sed -e 's,^P[0-9A-Ca-c],P,' -e 's,[0-9],,g' | sort | uniq -c | sort -rn | head -1 | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` # No boring digicam-style filenames in this dir? if [ -z "$t" ] then return fi # More than 70% of the filenames are identical sans numbers? Probably # from a digicam or some other uninteresting filename. count=`echo "$t" | cut -d ' ' -f1 | sed 's,[^0-9],,g'` boring_pattern=`echo "$t" | sed -e 's,^[ ]*[0-9]*[ ]*,,g'` count=`expr "$count" \* 100` if [ `expr "$count" / $GLOBAL_total_image_count` -ge 70 ] then GLOBAL_heuristic_filenames_are_digicam_boring=1 GLOBAL_heuristic_filename_pattern="$boring_pattern" fi empty_count=`cat "$GLOBAL_dates_filename" | egrep -v '^#|^$' | awk -F\| '{if (NF == 3) {print $2}}' | grep '^[ ]*$' | wc -l | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` same_day_count=`cat "$GLOBAL_dates_filename" | egrep -v '^#|^$' | awk -F\| '{if (NF == 3) {print $2}}' | grep -v '^[ ]*$' | sort | uniq -c | sort -rn | head -1 | sed -e 's,^[ ]*\([0-9]*\).*,\1,'` # More than 80% of the image's days are identical? # Less than 20% of the image's times are missing? Note it. if [ -n "$empty_count" -a -n "$same_day_count" ] then empty_count=`expr "$empty_count" \* 100` same_day_count=`expr "$same_day_count" \* 100` if [ `expr "$empty_count" / $GLOBAL_total_image_count` -le 20 -a \ `expr "$same_day_count" / $GLOBAL_total_image_count` -ge 80 ] then GLOBAL_heuristic_days_are_identical=1 fi fi empty_count=`cat "$GLOBAL_dates_filename" | egrep -v '^#|^$' | awk -F\| '{if (NF == 3) {print $3}}' | grep '^[ ]*$' | wc -l | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,'` empty_count=`expr "$empty_count" \* 100` if [ `expr "$empty_count" / $GLOBAL_total_image_count` -gt 80 ] then GLOBAL_heuristic_times_are_absent=0 fi } iso8601_format_datestr_to_human () { local date year month day t date="$*" RETURN_human_date="$date" if echo "$date" | egrep '^(19|20)[0-9][0-9]-[01][0-9]-[0-3][0-9]$' >/dev/null 2>&1 then : else return fi year=`echo "$date" | sed 's,.*\([12][09][0-9][0-9]\)-\([01][0-9]\)-\([0-3][0-9]\).*,\1,'` month=`echo "$date" | sed 's,.*\([12][09][0-9][0-9]\)-\([01][0-9]\)-\([0-3][0-9]\).*,\2,'` day=`echo "$date" | sed 's,.*\([12][09][0-9][0-9]\)-\([01][0-9]\)-\([0-3][0-9]\).*,\3,'` month=`echo "$month" | sed \ -e "s,^01\$,$GLOBAL_monthname_01_text," -e "s,^02\$,$GLOBAL_monthname_02_text," \ -e "s,^03\$,$GLOBAL_monthname_03_text," -e "s,^04\$,$GLOBAL_monthname_04_text," \ -e "s,^05\$,$GLOBAL_monthname_05_text," -e "s,^06\$,$GLOBAL_monthname_06_text," \ -e "s,^07\$,$GLOBAL_monthname_07_text," -e "s,^08\$,$GLOBAL_monthname_08_text," \ -e "s,^09\$,$GLOBAL_monthname_09_text," -e "s,^10\$,$GLOBAL_monthname_10_text," \ -e "s,^11\$,$GLOBAL_monthname_11_text," -e "s,^12\$,$GLOBAL_monthname_12_text,"` month=`echo "$month" | sed -e 's, ,\ ,g'` day=`echo "$day" | sed 's,^0,,'` if [ -n "$year" -a -n "$month" -a -n "$day" ] then t=`echo "$GLOBAL_date_formatting_text" | sed -e "s,@MONTH@,${month}," -e "s,@DAY@,${day}," -e "s,@YEAR@,${year},"` RETURN_human_date=`echo "$t" | sed 's, ,\ ,g'` fi } ######################################################### #### Get EXIF et al information and add it to slideshow pages ######################################################### # This function should add the photo information using a variety # of programs to recover the EXIF data embedded in the JPEG. # # The order of precedence is currently # INFO.TXT -> jhead -> metacam -> dump-exif # # But I think metacam is seing the most active development and it will # probably come before jhead in the future. print_image_exif_info () { local fn html fn="$*" image_name_to_html_name "$fn" html="$RETURN_html_name" generated_name_to_source_name "$fn" fn="$RETURN_source_name" print_libexif_info "$fn" [ $RETURN_printed_something -eq 1 ] && return print_coolpix_info_txt "$fn" [ $RETURN_printed_something -eq 1 ] && return print_jhead_info "$fn" [ $RETURN_printed_something -eq 1 ] && return print_metacam_info "$fn" [ $RETURN_printed_something -eq 1 ] && return print_gphoto_exifdump_info "$fn" [ $RETURN_printed_something -eq 1 ] && return print_dumpexif_info "$fn" [ $RETURN_printed_something -eq 1 ] && return } print_coolpix_info_txt () { local fn RETURN_printed_something=0 fn="$*" get_coolpix_info_txt "$fn" if [ -n "$RETURN_info_txt" ] then echo "" echo "<!-- Information about this photo from the Nikon Coolpix INFO.TXT record -->" if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "<!--" fi echo "$RETURN_info_txt" if [ $GLOBAL_show_image_info -eq 1 ] then echo "</pre>" else echo "-->" fi echo "" RETURN_printed_something=1 fi } print_jhead_info () { local fn RETURN_printed_something=0 fn="$*" [ $GLOBAL_jhead_is_present -eq 0 ] && return if jhead "$fn" 2>&1 | grep -i 'Exposure time *:' >/dev/null then echo "" echo "<!-- Information about this photo from jhead -->" if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "<!--" fi jhead "$fn" 2>/dev/null | grep -v 'File date.*:' | grep -v '^$' | sed 's,^, ,' if [ $GLOBAL_show_image_info -eq 1 ] then echo "</pre>" else echo "-->" fi echo "" RETURN_printed_something=1 fi } # "exif" is from the libexif project: # http://www.sourceforge.net/projects/libexif # It's comprehensive but not so pretty without a little reformatting # Output looks like this in rev 0.4: #--------------------+----------------------------------------------------------- #Tag |Value #--------------------+----------------------------------------------------------- #Image Description | #Manufacturer |NIKON CORPORATION #Model |NIKON D100 #x-Resolution |300/1 #y-Resolution |300/1 #Resolution Unit |Inch #Software |Ver.0.32 #Date and Time |2002:05:16 00:19:17 #YCbCr Positioning |co-sited # [...] # etc print_libexif_info () { local fn RETURN_printed_something=0 fn="$*" [ $GLOBAL_exif_is_present -eq 0 ] && return if exif "$fn" 2>&1 | grep -i 'Image Description' > /dev/null then echo "" echo "<!-- Information about this photo from exif -->" if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "<!--" fi exif "$fn" 2>/dev/null | egrep 'Manufacturer|Model|Software|Date and time|Exposure Time|FNumber|ExposureProgram|ISO Speed Ratings|Exif Version|Exposure Bias|MaxApertureValue|Metering Mode|Light Source|Flash|Focal Length|Color Space|Scene Type|Subject Distance|Digital Zoom Ratio|Scene Capture Type|Gain Control|Contrast|Saturation|Sharpness|White Balance|Exposure Mode' | grep -v '[|][ ]*$' | grep -v '^[ ]*$' | sed -e 's,[ ]*$,,' -e 's,|, : ,' -e 's,^, ,' | grep -v 'Unknown[ ]*$' | grep -iv 'Digital Zoom Ratio.* 1/1$' | grep -vi 'Exposure Bias.* 0\.0$' | egrep -vi FlashPixVersion | egrep -iv '(Gain Control|Saturdation|Contrast|Sharpness).* Normal$' | egrep -iv 'Scene Capture Type.* Standard$' | egrep -iv 'MaxApertureValue.* [0-9]+/[0-9]+$' if [ $GLOBAL_show_image_info -eq 1 ] then echo "</pre>" else echo "-->" fi echo "" RETURN_printed_something=1 fi } print_metacam_info () { local fn RETURN_printed_something=0 fn="$*" [ $GLOBAL_metacam_is_present -eq 0 ] && return if metacam "$fn" 2>&1 | grep -i 'Focal Length: ' >/dev/null then echo "" echo "<!-- Information about this photo from metacam -->" if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "<!--" fi metacam "$fn" 2>/dev/null | grep -v '^$' | sed -e 's,.*Standard Fields -.*,::: Standard Fields :::,' \ -e 's,.*EXIF Fields -.*,::: EXIF Fields :::,' \ -e 's,.*Manufacturer Fields -.*,::: Manufacturer Fields :::,' | egrep -vi '^File:' | egrep -vi 'image capture date|image digitized date|component configuration|(x|y) resolution|ycbcr positioning|exif image (height|width)|flashpix ver|software version|compressed bits per pixel|firmware version|manufacturer fields|standard fields|exif fields|image description|sub-second .* time|nikon version number|sensing method|owner name|image type|image number|contrast.*normal|sharpness.*normal|saturation.*normal|drive mode.*normal|exif version|shutter speed value|colorspace.*sRGB|exposure bias:[^0-9]*0[^0-9]*$|image size|scene capture type: standard|^[ ]aperture value|macro mode.*no macro|focus mode.*single focus|exposure mode.*auto exposure' if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "-->" fi echo "" RETURN_printed_something=1 fi } print_gphoto_exifdump_info () { local fn fn="$*" RETURN_printed_something=0 [ $GLOBAL_gphotoexifdump_is_present -eq 0 ] && return get_gphoto_exifdump_info "$fn" if [ -n "$RETURN_gphoto_info" ] then echo "" echo "<!-- Information about this photo from gphoto-exifdump -->" if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "<!--" fi echo "$RETURN_gphoto_info" | grep -v '^$' | sed 's,^, ,' if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "-->" fi echo "" RETURN_printed_something=1 fi } # Convert this: # Tag 0x10F Make = 'SONY' # Tag 0x132 DateTime = '2001:10:31 13:45:19' # Tag 0x9205 MaxApertureValue = 28/10=2.8 # Into this: # Camera make : SONY # Date/Time : 2001:10:31 13:45:19 # Max Aperture : f/2.8 # gphoto 0.4.3's gphoto-exifdump outputs like this, but it's such a useless # format that I'd suspect they'll change it eventually... I'll need to keep # an eye on that or makethumbs could start inserting garbage into peoples # image galleries. get_gphoto_exifdump_info () { local fn t RETURN_gphoto_info="" fn="$*" [ $GLOBAL_gphotoexifdump_is_present -eq 0 ] && return t=`gphoto-exifdump "$fn" 2>/dev/null | egrep 'Make|Model|DateTime|ExposureTime|FNumber|ISOSpeedRatings|MaxApertureValue|Flash|FocalLength' | egrep -v 'MakerNote|FlashPixVersion' | sed 's,^Tag 0x[0-9a-fA-F]* *,,' | sed -e 's,DateTimeOriginal,DateTime,' -e 's,DateTimeDigitized,DateTime,' | sort | uniq | sed -e 's,^[ ]*,,' -e 's,[ ]*$,,' | sed -e "s,.*Make.*'\(.*\)'\$,a Camera make : \1," \ -e "s,.*Model.*'\(.*\)'\$,b Camera model : \1," \ -e "s,.*DateTime.*'\(.*\)'\$,c Date/Time : \1," \ -e 's,.*Flash = 0.*,d Flash : No,' \ -e 's,.*Flash = 1.*,d Flash : Yes,' \ -e 's,.*ExposureTime.*= *\([0-9.]*\)$,f Exposure time: \1s,' \ -e 's,.*FNumber.*= *\([0-9.]*\)$,g Aperture : f/\1,' \ -e 's,.*ISOSpeedRatings = *\([0-9.]*\)$,h ISO equiv. : \1,' \ -e 's,.*MaxApertureValue.*= *\([0-9.]*\)$,i Max Aperture : f/\1,' \ -e 's,.*FocalLength.*= *\([0-9.]*\)$,e Focal Length : \1mm,' | grep -v '^Tag' | sort | cut -d ' ' -f 2-` if [ -n "$t" ] then RETURN_gphoto_info="$t" fi } print_dumpexif_info () { local fn RETURN_printed_something=0 fn="$*" [ $GLOBAL_dumpexif_is_present -eq 0 ] && return if dump-exif "$fn" 2>&1 | grep -i 'ExposureTime:' >/dev/null then if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "<!-- Information about this photo from dump-exif:" fi dump-exif "$fn" 2>/dev/null | grep -v '^$' | sed 's,^, ,' if [ $GLOBAL_show_image_info -eq 1 ] then echo "<pre>" else echo "-->" fi echo "" RETURN_printed_something=1 fi } get_coolpix_info_txt () { local filename lineno filename="$*" RETURN_info_txt="" [ ! -f INFO.TXT ] && return generated_name_to_source_name "$filename" filename="$RETURN_source_name" if cat INFO.TXT | tr -d '\r' | grep -i "^$filename\$" >/dev/null 2>&1 then : else return fi lineno=`cat -n INFO.TXT | tr -d '\r' | grep -i "^[ ]*[0-9]*[ ]*$filename\$" | cut -f1 | sed 's,[^0-9],,g'` RETURN_info_txt=`cat INFO.TXT | tr -d '\r' | sed "1,${lineno}d" | awk '{if (NF == 0) { exit } print " " $0;}' | grep -v '^[ ]*$'` } ######################################################### #### Try to get image time/date data via a variety of means ######################################################### get_image_time_date () { local fn generated_name_to_source_name "$*" fn="$RETURN_source_name" RETURN_time_date="" RETURN_date="" RETURN_time="" get_time_date_via_jhead "$fn" if [ -n "$RETURN_jhead_time_date" ] then RETURN_time_date="$RETURN_jhead_time_date" RETURN_date="$RETURN_jhead_date" RETURN_time="$RETURN_jhead_time" return fi get_time_date_via_strings "$fn" if [ -n "$RETURN_strings_time_date" ] then RETURN_time_date="$RETURN_strings_time_date" RETURN_date="$RETURN_strings_date" RETURN_time="$RETURN_strings_time" return fi get_time_date_via_metacam "$fn" if [ -n "$RETURN_metacam_time_date" ] then RETURN_time_date="$RETURN_metacam_time_date" RETURN_date="$RETURN_metacam_date" RETURN_time="$RETURN_metacam_time" return fi get_time_date_via_exiftools "$fn" if [ -n "$RETURN_exiftools_time_date" ] then RETURN_time_date="$RETURN_exiftools_time_date" RETURN_date="$RETURN_exiftools_date" RETURN_time="$RETURN_exiftools_time" return fi get_coolpix_time_date "$fn" if [ -n "$RETURN_coolpix_time_date" ] then RETURN_time_date="$RETURN_coolpix_time_date" RETURN_date="$RETURN_coolpix_date" RETURN_time="$RETURN_coolpix_time" return fi get_chillcam_time_date "$fn" if [ -n "$RETURN_chillcam_time_date" ] then RETURN_time_date="$RETURN_chillcam_time_date" RETURN_date="$RETURN_chillcam_date" RETURN_time="$RETURN_chillcam_time" return fi get_webcam32_time_date "$fn" if [ -n "$RETURN_webcam32_time_date" ] then RETURN_time_date="$RETURN_webcam32_time_date" RETURN_date="$RETURN_webcam32_date" RETURN_time="$RETURN_webcam32_time" return fi get_date_via_preferred_filename "$fn" if [ -n "$RETURN_preferred_filename_time_date" ] then RETURN_time_date="$RETURN_preferred_filename_time_date" RETURN_date="$RETURN_preferred_filename_date" RETURN_time="$RETURN_preferred_filename_time" return fi get_date_via_filename_usa_specific "$fn" if [ -n "$RETURN_usa_time_date" ] then RETURN_time_date="$RETURN_usa_time_date" RETURN_date="$RETURN_usa_date" RETURN_time="$RETURN_usa_time" return fi guess_date_via_directory_name if [ -n "$RETURN_guessed_date" ] then RETURN_time_date="$RETURN_guessed_time_date" RETURN_date="$RETURN_guessed_date" RETURN_time="$RETURN_guessed_time" return fi get_date_via_filename_reluctantly "$fn" if [ -n "$RETURN_reluctant_time_date" ] then RETURN_time_date="$RETURN_reluctant_time_date" RETURN_date="$RETURN_reluctant_date" RETURN_time="$RETURN_reluctant_time" return fi } # Get one of these lines from metacam output : # # Image Creation Date: 2001:12:23 13:39:48 # Image Capture Date: 2001:12:23 13:39:48 # Image Digitized Date: 2001:12:23 13:39:48 # # All three may be present, or only one may be present. get_time_date_via_metacam () { local fn td [ $GLOBAL_metacam_is_present -eq 0 ] && return fn="$*" RETURN_metacam_time_date="" RETURN_metacam_date="" RETURN_metacam_time="" td=`metacam "$fn" 2>/dev/null | egrep -i 'Image (Creation|Capture|Digitized) Date' | grep -v '0000:00:00 00:00' | head -1 | awk '{print $(NF-1) " " $NF}' | sed -e 's,[^0-9: ],,g' \ -e 's,\([12][0-9][0-9][0-9]\):\([01][0-9]\):\([0-3][0-9]\) \([012][0-9]\):\([0-6][0-9]\).*,\1-\2-\3 \4:\5,'` if [ -n "$td" ] then RETURN_metacam_time_date="$td" RETURN_metacam_date=`echo "$td" | awk '{print $1}'` RETURN_metacam_time=`echo "$td" | awk '{print $2}'` fi } # Get this line from dump-exif (a part of exif-tools) output : # # DateTimeOriginal: 2000:04:19 15:01:56 get_time_date_via_exiftools () { local fn td [ $GLOBAL_dumpexif_is_present -eq 0 ] && return fn="$*" RETURN_exiftools_time_date="" RETURN_exiftools_date="" RETURN_exiftools_time="" td=`dump-exif "$fn" 2>/dev/null | egrep -i 'DateTimeOriginal' | grep -v '0000:00:00 00:00' | head -1 | awk '{print $(NF-1) " " $NF}' | sed -e 's,[^0-9: ],,g' \ -e 's,\([12][0-9][0-9][0-9]\):\([01][0-9]\):\([0-3][0-9]\) \([012][0-9]\):\([0-6][0-9]\).*,\1-\2-\3 \4:\5,'` if [ -n "$td" ] then RETURN_exiftools_time_date="$td" RETURN_exiftools_date=`echo "$td" | awk '{print $1}'` RETURN_exiftools_time=`echo "$td" | awk '{print $2}'` fi } # Get this line from jhead output : # # Date/Time : 2000:04:30 16:26:56 get_time_date_via_jhead () { local fn td [ $GLOBAL_jhead_is_present -eq 0 ] && return fn="$*" RETURN_jhead_time_date="" RETURN_jhead_date="" RETURN_jhead_time="" td=`jhead "$fn" 2>/dev/null | egrep -i 'Date/Time *:' | grep -v '0000:00:00 00:00' | head -1 | awk '{print $(NF-1) " " $NF}' | sed -e 's,[^0-9: ],,g' \ -e 's,\([12][0-9][0-9][0-9]\):\([01][0-9]\):\([0-3][0-9]\) \([012][0-9]\):\([0-6][0-9]\).*,\1-\2-\3 \4:\5,'` if [ -n "$td" ] then RETURN_jhead_time_date="$td" RETURN_jhead_date=`echo "$td" | awk '{print $1}'` RETURN_jhead_time=`echo "$td" | awk '{print $2}'` fi } get_coolpix_time_date () { local fn timestamp fn="$*" RETURN_coolpix_time_date="" RETURN_coolpix_date="" RETURN_coolpix_time="" get_coolpix_info_txt "$fn" if [ -n "$RETURN_info_txt" ] then timestamp=`echo "$RETURN_info_txt" | grep '^[ ]*DATE' | awk '{print $3 " " $4}' | sed 's,\.,-,g' | sed 's,[^0-9 :-],,g'` if [ -n "$timestamp" ] then RETURN_coolpix_time_date="$timestamp" RETURN_coolpix_date=`echo "$timestamp" | awk '{print $1}'` RETURN_coolpix_time=`echo "$timestamp" | awk '{print $2}'` return fi fi } # See if we can find the date string with good old strings(1). # The date is in there in the form of 2001:11:19 19:25:09 get_time_date_via_strings () { local fn="$*" local td RETURN_strings_time_date="" RETURN_strings_date="" RETURN_strings_time="" # Use dd if it is available to just read the first 2kbytes of the file - # if there's a date string in the EXIF header, it'll be there. I used # to just run 'strings' on the file, but that would take over twice as # long as just reading the first block. td=`(if [ $GLOBAL_dd_works_well -eq 1 ]; then dd bs=2048 count=1 <"$fn" 2>/dev/null; else cat < "$fn"; fi )| strings 2>/dev/null | grep -v '0000:00:00 00:00' | egrep '(19|20)[0-9][0-9]:(0[1-9]|1[0-2]):[0-3][0-9] [0-2][0-9]:[0-6][0-9]:[0-6][0-9]' | sort | uniq -c | sort -rn | sed 's,^[ ]+[0-9]+[ ]+,,' | head -1 | awk '{print $(NF-1) " " $NF}' | sed -e 's,[^0-9: ],,g' \ -e 's,\([12][0-9][0-9][0-9]\):\([01][0-9]\):\([0-3][0-9]\) \([012][0-9]\):\([0-6][0-9]\).*,\1-\2-\3 \4:\5,'` if [ -n "$td" ] then RETURN_strings_time_date="$td" RETURN_strings_date=`echo "$td" | awk '{print $1}'` RETURN_strings_time=`echo "$td" | awk '{print $2}'` fi } # Olympus cameras (C2000Z, C2020Z, C3030Z) have photo filenames in the form of # P<MONTH-DIGIT><DAY><4-DIGIT-SEQ-NO>.jpg # If a person hasn't set the time/date in the camera, MONTH-DIGIT (a hex # dgit) and DAY are 1, giving you a name of the form P101xxxx.jpg, which # should be ignored. Yes, this means that photos taken on New Year's Day # will not be recognized. get_olympus_filename_date () { local fn="$*" local month day RETURN_olympus_time_date="" RETURN_olympus_date="" RETURN_olympus_time="" if echo "$fn" | egrep '^P[1-9A-Ca-c][0-3][0-9][0-9][0-9][0-9][0-9].jpg$' >/dev/null 2>&1 then month=`echo "$fn" | sed -e 's,^P\(.\).*,\1,' \ -e "s,^1\$,$GLOBAL_monthname_01_text," -e "s,^2\$,$GLOBAL_monthname_02_text," \ -e "s,^3\$,$GLOBAL_monthname_03_text," -e "s,^4\$,$GLOBAL_monthname_04_text," \ -e "s,^5\$,$GLOBAL_monthname_05_text," -e "s,^6\$,$GLOBAL_monthname_06_text," \ -e "s,^7\$,$GLOBAL_monthname_07_text," -e "s,^8\$,$GLOBAL_monthname_08_text," \ -e "s,^9\$,$GLOBAL_monthname_09_text," -e "s,^[Aa]\$,$GLOBAL_monthname_10_text," \ -e "s,^[Bb]\$,$GLOBAL_monthname_11_text," -e "s,^[Cc]\$,$GLOBAL_monthname_12_text,"` day=`echo "$fn" | sed -e 's,^P.\([0-3][0-9]\).*,\1,' -e 's,^0,,'` if [ -n "$month" -a -n "$day" ] then [ "$month" = January -a "$day" = "1" ] && return [ "$month" = 0 -a "$day" = "0" ] && return RETURN_olympus_time_date="$month $day" RETURN_olympus_date="$month $day" RETURN_olympus_time="" fi fi } # This only tries to recognize dates in some form of YYYY-MM-DD. # Americans might expect MM-DD-YYYY to work, or MM-DD-YY, but # it's not globally unambiguous, so those Americans may now learn # the joys of ISO 8601. Unless I feel like adding support for # the US formats some day. Don't hold your breath. guess_date_via_directory_name () { local t [ $GLOBAL_already_failed_guessing_date_from_dir -eq 1 ] && return t=`pwd` t=`basename "$t"` RETURN_guessed_time_date="" RETURN_guessed_date="" RETURN_guessed_time="" # Don't re-do work we've aready done during this makethumbs run. if [ -n "$GLOBAL_static_var_guessed_date_from_dirname" ] then RETURN_guessed_time_date="$GLOBAL_static_var_guessed_date_from_dirname" RETURN_guessed_date="$GLOBAL_static_var_guessed_date_from_dirname" RETURN_guessed_time="" return fi iso8601_date_decoder "$t" if [ -n "$RETURN_iso8601_time_date" ] then RETURN_guessed_time_date="$RETURN_iso8601_time_date" RETURN_guessed_date="$RETURN_iso8601_date" RETURN_guessed_time="$RETURN_iso8601_time" GLOBAL_static_var_guessed_date_from_dirname="$RETURN_iso8601_time_date" else GLOBAL_already_failed_guessing_date_from_dir=1 fi } get_date_via_preferred_filename () { local fn fn="$*" RETURN_preferred_filename_time_date="" RETURN_preferred_filename_date="" RETURN_preferred_filename_time="" iso8601_date_decoder "$fn" if [ -n "$RETURN_iso8601_time_date" ] then RETURN_preferred_filename_time_date="$RETURN_iso8601_time_date" RETURN_preferred_filename_date="$RETURN_iso8601_date" RETURN_preferred_filename_time="$RETURN_iso8601_time" return fi } get_date_via_filename_usa_specific () { local fn fn="$*" RETURN_usa_time_date="" RETURN_usa_date="" RETURN_usa_time="" [ $GLOBAL_usa_specific_date_format_checks -eq 0 ] && return try_ians_filename_format "$fn" if [ -n "$RETURN_ians_date" ] then RETURN_usa_time_date="$RETURN_ians_time_date" RETURN_usa_date="$RETURN_ians_date" RETURN_usa_time="$RETURN_ians_time" return fi try_kristens_filename_format "$fn" if [ -n "$RETURN_kristens_date" ] then RETURN_usa_time_date="$RETURN_kristens_time_date" RETURN_usa_date="$RETURN_kristens_date" RETURN_usa_time="$RETURN_kristens_time" return fi try_erics_filename_format "$fn" if [ -n "$RETURN_erics_date" ] then RETURN_usa_time_date="$RETURN_erics_time_date" RETURN_usa_date="$RETURN_erics_date" RETURN_usa_time="$RETURN_erics_time" return fi } get_date_via_filename_reluctantly () { local fn fn="$*" RETURN_reluctant_time_date="" RETURN_reluctant_date="" RETURN_reluctant_time="" get_olympus_filename_date "$fn" if [ -n "$RETURN_olympus_date" ] then RETURN_reluctant_time_date="$RETURN_olympus_time_date" RETURN_reluctant_date="$RETURN_olympus_date" RETURN_reluctant_time="$RETURN_olympus_time" return fi } iso8601_date_decoder () { local name canonical_fmt t local fmt1 fmt2 fmt3 fmt4 fmt5 fmt6 fmt7 fmt8 name="$*" RETURN_iso8601_time_date="" RETURN_iso8601_time="" RETURN_iso8601_date="" canonical_fmt="[12][09][0-9][0-9]-[012][0-9]-[0-3][0-9]" fmt1="$canonical_fmt" fmt2="[12][09][0-9][0-9]-[012]?[0-9]-[0-3]?[0-9]" fmt3="[12][09][0-9][0-9]_[012][0-9]_[0-3][0-9]" fmt4="[12][09][0-9][0-9]_[012]?[0-9]_[0-3]?[0-9]" fmt5="[12][09][0-9][0-9]\.[012][0-9]\.[0-3][0-9]" fmt6="[12][09][0-9][0-9]\.[012]?[0-9]\.[0-3]?[0-9]" fmt7="[12][09][0-9][0-9] [012]?[0-9] [0-3]?[0-9]" fmt8="[12][09][0-9][0-9][012][0-9][0-3][0-9]" for exp in "$fmt1" "$fmt2" "$fmt3" "$fmt4" "$fmt5" "$fmt6" "$fmt7" "$fmt8" do if echo "$name" | egrep "$exp" >/dev/null 2>&1 then # Old fashioned awk's won't support gensub(), so use perl # as a backup. I hate to do it, but gsub won't do what # I need as near as I can tell... if [ $GLOBAL_date_parser_program = "awk" ] then t=`echo "$name" | sed "s/.*\(${exp}\).*/\1/" | $AWK '{\ datepat="^.*\([12][09][0-9][0-9]\)[^0-9]*\([01]?[0-9]\)[^0-9]*\([0-3]?[0-9]\)[^0-9]?.*$" year = gensub (datepat, "\\\\1", "g", $0); month = gensub (datepat, "\\\\2", "g", $0); day = gensub (datepat, "\\\\3", "g", $0); printf ("%s-%02d-%02d", year, month, day); }'` else t=`echo "$name" | sed "s/.*\(${exp}\).*/\1/" | perl -ne \ 'if (/^.*([12][09]\d\d)[^\d]*([01]?\d)[^\d]*([0-3]?[\d])[^\d]?.*$/) {printf ("%s-%02d-%02d\n", $1, $2, $3); }'` fi if echo "$t" | egrep "$canonical_fmt" >/dev/null 2>&1 then RETURN_iso8601_time_date="$t" RETURN_iso8601_date="$t" RETURN_iso8601_time="" return fi fi done } # ChillCAM is a web cam program that embeds the time/date in the comment field. # www.chillcam.com # format is "MM/DD/YYYY HH:MM:SS" get_chillcam_time_date () { local fn t fn="$*" t="" RETURN_chillcam_time_date="" RETURN_chillcam_date="" RETURN_chillcam_time="" if [ $GLOBAL_rdjpgcom_is_present -eq 0 -a \ $GLOBAL_jhead_is_present -eq 0 -a \ $GLOBAL_jpegtopnm_is_present -eq 0 ] then return fi if [ $GLOBAL_rdjpgcom_is_present -eq 1 ] then t=`rdjpgcom "$fn" 2>/dev/null | grep -i chillcam | head -1` fi if [ -z "$t" -a $GLOBAL_jhead_is_present -eq 1 ] then t=`jhead "$fn" 2>/dev/null | grep -i chillcam | head -1` fi if [ -z "$t" -a $GLOBAL_jpegtopnm_is_present -eq 1 ] then t=`jpegtopnm -comment "$fn" 2>&1 >/dev/null | grep -i chillcam | head -1` fi [ -z "$t" ] && return t=`echo "$t" | awk '{print $(NF-1) " " $NF}' | sed -e 's,[^0-9/: ],,g' | grep '[01][0-9]/[0-3][0-9]/[12][0-9][0-9][0-9] [0-2][0-9]:[0-6][0-9]:[0-6][0-9]' | sed -e 's,\([01][0-9]\)/\([0-3][0-9]\)/\([12][0-9][0-9][0-9]\) \([012][0-9]\):\([0-6][0-9]\).*,\3-\1-\2 \4:\5,'` [ -z "$t" ] && return RETURN_chillcam_time_date="$t" RETURN_chillcam_date=`echo "$t" | awk '{print $1}'` RETURN_chillcam_time=`echo "$t" | awk '{print $2}'` } # Webcam32 is some webcam snapshot program that puts the time/date in a # jpeg comment. www.webcam32.com. # format is "MM/DD/YY HH:MM:SS" in typical USA fashion. get_webcam32_time_date () { local fn t fn="$*" t="" RETURN_chillcam_time_date="" RETURN_chillcam_date="" RETURN_chillcam_time="" if [ $GLOBAL_rdjpgcom_is_present -eq 0 -a \ $GLOBAL_jhead_is_present -eq 0 -a \ $GLOBAL_jpegtopnm_is_present -eq 0 ] then return fi if [ $GLOBAL_rdjpgcom_is_present -eq 1 ] then t=`rdjpgcom "$fn" 2>/dev/null | grep -i webcam32 | head -1` fi if [ -z "$t" -a $GLOBAL_jhead_is_present -eq 1 ] then t=`jhead "$fn" 2>/dev/null | grep -i webcam32 | head -1` fi if [ -z "$t" -a $GLOBAL_jpegtopnm_is_present -eq 1 ] then t=`jpegtopnm -comment "$fn" 2>&1 >/dev/null | grep -i webcam32 | head -1` fi [ -z "$t" ] && return t=`echo "$t" | awk '{print $(NF-1) " " $NF}' | sed -e 's,[^0-9/: ],,g' | grep '[01][0-9]/[0-3][0-9]/[0-9][0-9] [0-2][0-9]:[0-6][0-9]:[0-6][0-9]' | sed -e 's,\([01][0-9]\)/\([0-3][0-9]\)/\([0-8][0-9]\) \([012][0-9]\):\([0-6][0-9]\).*,20\3-\1-\2 \4:\5,' \ -e 's,\([01][0-9]\)/\([0-3][0-9]\)/\([9][0-9]\) \([012][0-9]\):\([0-6][0-9]\).*,19\3-\1-\2 \4:\5,'` [ -z "$t" ] && return RETURN_webcam32_time_date="$t" RETURN_webcam32_date=`echo "$t" | awk '{print $1}'` RETURN_webcam32_time=`echo "$t" | awk '{print $2}'` } #### USA SPECIFIC DATE FORMAT CHECKS ## Dates can be ordered by month/day/year or they can be ordered by ## day/month/year, depending on which part of the world you live in. ## I'd rather everyone use an ISO8601 unambiguous date, but they don't. ## The following timestamp checks all assume a US (MM/DD/year) format ## date string, and they can all be disabled by setting ## usa_specific_date_format_checks to zero in your .makethumbsrc file. # Ian Lance Taylor has his images named MMDDYY-n+.jpg. # (where 'n' is the sequence number on that day). # I cheat and assume that years "00" - "29" are 2000-2029 and years # "70-99" are 1970-1999. try_ians_filename_format () { local fn t fn="$*" RETURN_ians_time_date="" RETURN_ians_date="" RETURN_ians_time="" [ $GLOBAL_usa_specific_date_format_checks -eq 0 ] && return if echo "$fn" | egrep '^(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[0-27-9][0-9]-[1-9][0-9]*\.(jpg|gif|png)$' >/dev/null 2>&1 then t=`echo "$fn" | sed -e 's|^\([01][0-9]\)\([0-3][0-9]\)\([0-2][0-9]\).*|20\3-\1-\2|' \ -e 's|^\([01][0-9]\)\([0-3][0-9]\)\([7-9][0-9]\).*|19\3-\1-\2|'` if [ -n "$t" ] then if echo "$t" | egrep '^(19|20)[0-9][0-9]-[01][0-9]-[0-3][0-9]$' >/dev/null 2>&1 then RETURN_ians_time_date="$t" RETURN_ians_date="$t" RETURN_ians_time="" return fi fi fi } # Eric Perlman names his files with the date encoded in the filename # in a format like "foo_(3-2-2002).jpg". (i.e. *MM-DD-YYYY* s.t. MM and DD # can be single digit.) try_erics_filename_format () { local fn t month day year fn="$*" RETURN_erics_time_date="" RETURN_erics_date="" RETURN_erics_time="" [ $GLOBAL_usa_specific_date_format_checks -eq 0 ] && return if echo "$fn" | egrep '[(](0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])-(19|20)[0-9][0-9][)]' >/dev/null 2>&1 then year=`echo "$fn" | sed -e 's|.*[(]\([0-9]*\)-\([0-9]*\)-\([12][09][0-9][0-9]\)[)].*|\3|'` month=`echo "$fn" | sed -e 's|.*[(]\([0-9]*\)-\([0-9]*\)-\([12][09][0-9][0-9]\)[)].*|\1|'` day=`echo "$fn" | sed -e 's|.*[(]\([0-9]*\)-\([0-9]*\)-\([12][09][0-9][0-9]\)[)].*|\2|'` t=`printf "%s-%02d-%02d" "$year" "$month" "$day" 2>/dev/null` if [ -n "$t" ] then if echo "$t" | egrep '^(19|20)[0-9][0-9]-[01][0-9]-[0-3][0-9]$' >/dev/null 2>&1 then RETURN_erics_time_date="$t" RETURN_erics_date="$t" RETURN_erics_time="" return fi fi fi } # Kristen Kakos has her images named MMDDYY_<name>.jpg. # I cheat and assume that years "00" - "29" are 2000-2029 and years # "70-99" are 1970-1999. try_kristens_filename_format () { local fn t fn="$*" RETURN_kristens_time_date="" RETURN_kristens_date="" RETURN_kristens_time="" [ $GLOBAL_usa_specific_date_format_checks -eq 0 ] && return if echo "$fn" | egrep '^(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[0-27-9][0-9]_[a-zA-Z1-9].*\.(jpg|gif|png)$' >/dev/null 2>&1 then t=`echo "$fn" | sed -e 's|^\([01][0-9]\)\([0-3][0-9]\)\([0-2][0-9]\).*|20\3-\1-\2|' \ -e 's|^\([01][0-9]\)\([0-3][0-9]\)\([7-9][0-9]\).*|19\3-\1-\2|'` if [ -n "$t" ] then if echo "$t" | egrep '^(19|20)[0-9][0-9]-[01][0-9]-[0-3][0-9]$' >/dev/null 2>&1 then RETURN_kristens_time_date="$t" RETURN_kristens_date="$t" RETURN_kristens_time="" return fi fi fi } ######################################################### #### call to main ######################################################### main ${1+"$@"}