#!/usr/local/bin/perl # # Anthony's WWW Counter (v1.3) # # Increment a counter ".cnt" file and generate a gif file of the number # using a set of pre-prepared images and the PbmPlus Image Filter Package. # A user ".dat" file file can specify the format and the location of these # images to something other than the default odometer. # # With the following call to the counter program the counter program # will require, or optionally use the following files # # /cgi-bin/counter/~username/sub-directory/filename.gif # `-------+------'`------------------+------------'`--< # | | `. # CGI Script Program | | # ,-------------- WWW Path to the counter files | # | Suffix must be ".gif" # V # filename.cnt REQUIRED: This is the file (in the above WWW path # directory -- note suffix change) which is used to # hold the actual count. Must be writable by everyone. # # filename.dat OPTIONAL: file which if present contains perl # variable settings to modify the output of counter. # For example here are some common variable (Default) # $FORMAT printf format sting ("%08u") # $FONT_DIR directory of digit images # (sub-dir "counter_images" in cgi-bin location) # $FONT_SUBDIR sub-dir relative to above ("odometer") # $FONT_PFX digit file prefix ("") # $FONT_SFX digit file suffix (".gif") # $MAGICK ImageMagick Image filter path # $PBMPLUS PbmPlus Image filter path # $GS Ghostscript location (for imagemagick error messages) # # NOTE: The FONT_SFX must be for ".ppm" images if only PbmPlus # filters are available. ImageMagic image filters can use almost # any image format of the individual images. Also other text # characters can be output as part of the counter, IF appropriate # character font images are available. # # Here is an example `.dat' file. # --------------8<------------- # $FORMAT = "%6u"; # counter is 6 digits with leading spaces # $FONT_SUBDIR = "bubble"; # Use a bubble font # --------------8<------------- # Which should be enough specify the counter format and to use a # different `font' for the counter. Remember the size of the images # is not required by this script. # # An this is a more complex example getting images from a personal # sub-directory the user has prepared. # --------------8<------------- # $FORMAT = "- %u -"; # counter is 6 digits with leading spaces # $FONT_DIR = "/home/anthony/digits"; # digits are stored here # $FONT_SUBDIR = ""; # no sub-directory under digits # $FONT_PFX = "bubble_"; # $FONT_SFX = ".gif"; # --------------8<------------- # EG: the counter image are stored in the directory "/home/user/digits" # with the number digits as filenames "bubble_0.gif" to "bubble_9.gif", # space as "bubble__.gif", and the character `@' as some special image # in the file named "bubble_@.gif". # # Also If you make copy (or symbolic link) to this program called # "counter-noinc", then a call to that cgi-bin program will NOT # increment the counter. # # This method of counter generation is very versital. You can add letters # and other images/symbols in the output counter image by adding them into # the $FORMAT string. In future other `increment' subroutines may be defined # to allow, say the current `time' or other information to be output # instead, given the appropraite images to use. # # Copyright # Anthony Thyssen 22 March 1996 # # 9 May 1998 -- Converted to use imagemagick filters instead of PbmPlus # image filters so as to allow almost amy image format input # and opened the format of the `.dat' file; # # 23 August 1999 -- Added extra Imagemagick executable locations and workout # of required shared libraries for these exectuables. # # 26 August 1998 -- Modified by Kim Taylor to handle sub directories in # combination with prefixes. (Good idea, thanks Kim - AT) # # ---------------------------------------------------------------------------- # INSTALLATION INSTRUCTIONS # # 1/ Create your font images images for 0 to 9 and a space. These should # be all the same height for proper concatanation and maybe the same # width so the output image size can also be given (for image width and # height attributes). The program itself does not really care. # # 2/ Save the 10 digits and space character in a directory in GIF # image format. Transparency will be preserved by imagemagick # filters used to concatanate the images into the final counter. # # EG odo_0.gif odo_1.gif ..... odo_9.gif odo__.gif # # The last is the space character which may or may not be required # depending on what format you use (%8u needs it %07 doesn't) # If you plan include text or hexadecimal in the counter format you # will need the graphic images for those characters used. # # 3/ Save this counter perl script into a cgi-bin as "counter" # also created the folloing symbolic link in this directory. # ln -s counter counter-noinc # which is the ``no increment'' link for the counter program. # # 4/ Modify the counter script for use on your site # FORMAT - default printf format string (length and style of count) # FONT_DIR - default directory holding ALL the ``Digit Images'' # FONT_SUBDIR - sub-dir relative to the above (usally "odometer") # FONT_PFX - the suffix used in images (usally "") # FONT_SFX - the suffix used in images (usally ".gif") # TRANSPARENT - color to make transparent (usally "#BFBFBF") # MAGICK - where the ImageMagick filters are located (override) # PBMPLUS - where the Pbmplus filters are located (override) # Note: # COUNTER - the name of the counter data file is worked out from # URL path information. (suffix .gif -> .cnt) # A simular method is used for the `data' file which controls the # appearence of the output image. # # 5/ Just create a empty counter file writable by ANYONE ending in ".cnt". # The URL you use to call this CGI script must include the URL path # to this file but with the suffix ".cnt" replaced with the suffix # ".gif" (see call above). The ".cnt" file must have write # permission for the user the Web Server runs as, generally the user # `nobody', or under linux, `wwwrun'. # # PS: Please mail any changes and updates back to the author. # # Various sets of digit images are available from the AIcons Library # http://www.cit.gu.edu.au/~anthony/icons in the prog/fonts directory # # Anthony Thyssen 22 March 1996 # # ---------------------------------------------------------------------------- # Specific Settings # NOTE:- All these settings can be overridden by the counter formating # control file `.dat' # Locate the counter '.cnt' file and the user format file '.dat' # from the extra `PATH' information supplied as part of the URL # to this CGI script. # NOTE that `.gif' suffix provided is igonred and is for the browsers # benift only. $COUNTER = $COUNT_DAT = $COUNT_GIF = $ENV{'PATH_TRANSLATED'}; $COUNTER =~ s/\.gif$/.cnt/; # convert suffix for counter file $COUNT_DAT =~ s/\.gif$/.dat/; # convert suffix extra formating data # Default Format and length of output (printf) Examples: %u %6u %06u $FORMAT = '%08u'; # Default location of where all the counter images are located # Images are "0.gif" to "9.gif" and "_.gif" for space # # Hardcorded location #$FONT_DIR = "/home/anthony/bin/cgi-bin/counter_images"; # # If "counter-images" directory is located in the `cgi-bin' directory $FONT_DIR = $ENV{SCRIPT_FILENAME}; # file path to this script! $FONT_DIR =~ s/^(.*)\/[^\/]+$/$1/; # directory of the `cgi-bin' $FONT_DIR .= "/counter_images"; # counter images this sub-dir # Set the default counter images to use from this sub-directory # EG: Image for a digit ($digit) would then be # $FONT_DIR/$FONT_SUBDIR/$FONT_PFX$digit$FONT_SFX # $FONT_SUBDIR = "odometer"; # sub-directory in counter_images directory $FONT_PFX = ""; # image prefix for digit images in sub-dir $FONT_SFX = ".gif"; # image suffix in the above directory $TRANSPARENT = "#BFBFBF"; # The transparency color of the digits # Image Filter Location (user '.dat' file can override) @search_path = ( "/usr/bin", "/usr/bin/X11", , , "/usr/local/bin", "/opt/bin", , , "/opt/imagemagick", "/opt/imagemagick/bin", "/opt/pbmplus", "/opt/pbmplus/bin" ); for $dir ( @search_path ) { $MAGICK = $dir if -f $dir."/montage"; # imagemagick filters $GS = $dir if -f $dir."/gs"; # Ghostscript (for errors) $PBMPLUS = $dir if -f $dir."/ppmtogif"; # Pbmplus filters } # Imagemagick shared libraries? $ENV{LD_LIBRARY_PATH} = "/usr/lib"; @search_path = ( "/usr/local/lib", "/opt/lib", , , "/opt/imagemagick/lib", "/usr/openwin/lib" ); for $dir ( @search_path ) { if ( -f $dir."/libMagick.so" || -f $dir."/libdpstk.so" || -f $dir."/libX11.so" || -f $dir."/libtiff.so" || -f $dir."/libpng.so" ) { $ENV{LD_LIBRARY_PATH} .= ":".$dir } } #print STDERR "MAGICK = $MAGICK\n"; #print STDERR "GS = $GS\n"; #print STDERR "PBMPLUS = $PBMPLUS\n"; #print STDERR "LD_LIBRARY_PATH = $ENV{LD_LIBRARY_PATH}\n"; # ------------------------------------------------------------- # Subroutines # Output Error Message a GIF image! sub Text { $text = join(" ", @_); print STDERR "Counter Error: ", $text, "\n"; print STDERR "For Counter image: $COUNT_GIF\n"; # PbmPlus Filters (prefered for this condition) if ( -f $PBMPLUS."/ppmtogif" ) { print "Content-type: image/gif\n\n"; # text -> bitmap -> gif system( "$PBMPLUS/pbmtext '$text' | $PBMPLUS/pnmdepth 255 2>/dev/null | $PBMPLUS/pgmtoppm '$TRANSPARENT' | $PBMPLUS/ppmtogif 2>/dev/null -transparent '$TRANSPARENT' " ); exit(0); } # ImageMagick Filter if ( -f $MAGICK."/convert" && defined $GS ) { # WARNING: this text to image conversion requires # ImageMagick version 4 or greater. # ensure ghostscript is on the PATH for text conversion $ENV{'PATH'} .= ":" . $GS; print "Content-type: image/gif\n\n"; system( "$MAGICK/convert +display 'label:$text' gif:-" ); exit(0); } # Emergency error output -- no text->image converter! print "Content-type: text/plain\n\n"; print $text, "\n"; exit(0); } # # Lock File Routine # USAGE: &lock(FILE,type) where type is either 'W', 'R' or 'U' # Using Fcntl Module (perl5) require "Fcntl.pm"; %lock_types = ( 'W', &Fcntl::F_WRLCK, # write lock 'R', &Fcntl::F_RDLCK, # read lock 'U', &Fcntl::F_UNLCK ); # unlock sub lock { local(*FILE, $type) = @_; local($lock) = pack('sslls', $lock_types{$type}, 0, 0, 0, 0); die ("fcntl_lock '$type': $!\n") if fcntl(*FILE, &Fcntl::F_SETLKW, $lock) == -1; } # ----------------------------------------------------------------------- # Main Routine # # test if we should NOT increment the counter? $NO_INCREMENT = 1 if $0 =~ /-noinc$/; # Local hacks -- ignore #$NO_INCREMENT = 1 if `uname -n` =~ /dragon/; #$NO_INCREMENT = 1 if `uname -n` =~ /lyrch/; #$NO_INCREMENT = 1 if `uname -n` =~ /kobold/; $| = 1; # auto-flush output if ( $NO_INCREMENT ) { # Just read the counter from `.cnt' -- don't increment if( ! open(COUNT, "<$COUNTER") ) { # open READ/write &Text("Read Error \"$COUNTER\": $!"); } &lock(COUNT, 'W'); # wait for an write lock $count = ; # read counter file `.cnt' &lock(COUNT, 'U'); # unlock counter close(COUNT); # close counter file } else { # Read and increment the counter file `.cnt' if( ! open(COUNT, "+<$COUNTER") ) { # open READ/write &Text("Read/Write Error \"$COUNTER\": $!"); } &lock(COUNT, 'W'); # wait for an write lock select((select(COUNT), $| = 1)[$[]); # set it to auto flush $count = ; # read the counter $count = sprintf("%u\n", $count+1); # increment counter seek(COUNT, 0, 0); # rewind counter printf COUNT $count; # save counter &lock(COUNT, 'U'); # unlock counter close(COUNT); # close counter file #truncate($COUNTER, length($COUNT)); # truncate rest of file } # &Text($count); # DEBUGGING: Check counter # ------ Format Modification ------ # Read the optional counter formating file `.dat' if ( -f $COUNT_DAT ) { &Text("Read Error \"$COUNT_DAT\": $!") if ! -r $COUNT_DAT; require( $COUNT_DAT ); } # ------ Sanity Checks ------ &Text("No image filter package path defined!") unless defined $MAGICK || defined $PBMPLUS; &Text("Image fonts must be \".ppm\" for PbmPlus image filters!") if $FONT_SFX ne ".ppm" && ! defined $MAGICK && defined $PBMPLUS; &Text("Imagemagick montage filters not found!") if defined $MAGICK && ! -x "$MAGICK/montage"; &Text("PbmPlus giftoppm filter not found!") if ! defined $MAGICK && defined $PBMPLUS && ! -x "$PBMPLUS/ppmtogif"; &Text("Counter output format \"$FORMAT\" not a printf format!") if $FORMAT !~ /%/; $FONT_DIR .= '/' if $FONT_DIR !~ /\/$/; # add directory `/' $FONT_DIR .= $FONT_SUBDIR; $FONT_DIR .= '/' if $FONT_DIR !~ /\/$/; # add directory `/' &Text("Font directory \"$FONT_DIR\" not an absolute file path!") if $FONT_DIR !~ /^\//; &Text("Font Directory \"$FONT_DIR\" not found!") unless -d $FONT_DIR; &Text("Font Directory \"$FONT_DIR\" not accessable!") unless -x _; $FONT_DIR .= '/' if $FONT_DIR !~ /\/$/; # add directory `/' &Text("Font digit \"${FONT_PFX}0$FONT_SFX\" not found!") unless -f $FONT_DIR . $FONT_PFX . "0" . $FONT_SFX; &Text("Font digit \"${FONT_PFX}0$FONT_SFX\" not readable!") unless -r _; # ----- Do the Task ---- # Format the counter with given format/length information $count = sprintf($FORMAT,$count); # &Text($count); # DEBUGGING: Check counter if ( $count =~ / / && ! -r $FONT_DIR . $FONT_PFX . "_" . $FONT_SFX ) { &Text("Missing needed space image \"$FONT_DIR${FONT_PFX}_$FONT_SFX\": $!"); } chdir $FONT_DIR; # to make it easier, jump to the font directory $digit_files = $count; $digit_files =~ s/ /_/g; # spaces -- use undersore $digit_files =~ s/./'$FONT_PFX$&$FONT_SFX' /g; # convert digits into filenames # &Text($digit_files); # DEBUGGING: Check filenames if ( defined $MAGICK ) { print "Content-type: image/gif\n\n"; #system( "$MAGICK/montage +display +frame +shadow +label +borderwidth \\ # -geometry +0+0 -tile 999x1 -compose in \\ # $digit_files gif:-" ); system( "$MAGICK/montage +display -mode concatenate -tile 999x1 \\ -transparency '$TRANSPARENT' \\ $digit_files gif:-" ); exit(0); } if ( defined $PBMPLUS ) { print "Content-type: image/gif\n\n"; system( "$PBMPLUS/pnmcat -lr $digit_files | $PBMPLUS/ppmtogif 2>/dev/null -transparent '$TRANSPARENT' " ); exit(0); } &Text("Assurence Failure - script should not reach this point!"); # ---------------------------------------------------------------------