#!/usr/bin/perl

#################################################################
# G-Counter v2.0
# by Adam Watkins
# wattyco@wattyo.com
# Distribute Freely
#################################################################
#################################################################
# Version History:
# v2.0 (5-02-01)
#	- Reworked the script with more error checking
#	- Completely reworked script
#	- Added format and inc option to query string
#	- Added Octal, Binary and Hexidecimal support
#	- Variable max digit support
#	- Added option to turn off on the fly counter adding
# v1.6 (2-20-01)
# 	-Change printing of the digits method to 1024 at a time
#	-Use a -e to see if count file exists first, if not create
#	-have it bomb out if if can't open count file
#################################################################
# How to set up?
#
# It can be displayed on yr page via Server Side Includes
# Just put the following in the into your html file
# <!--#include virtual="/cgi-bin/gcounter.pl?name&format&inc"-->
#
# name -> This is the counter name. This can allow you to run many independant counters off this one script.
#      It can be digits or text
# format -> This is the format the digits should be displayed in. d = Decimal, b = binary, h = hexidecimal, o = octal
#	This is just for fun. eg. gcounter.pl?name&d&inc will display the count in decimal. (this is recommended)	
# inc -> 1 - Increment count, 0 - do not increment count. This is used for if you are displaying all the different
#	number formats on the same page, you dont increment all of them (unless you want to)
#	*NOTE* If you display more than one count on a page make sure the one that loads LAST is the one that increments	
# So, the default way to do the ssi mode would be:
#	<!--#include virtual="/cgi-bin/gcounter.pl?name&d&1"-->
#
# LEGACY SUPPORT : this is equivalent to, legacy supported from older versions
#	<!--#include virtual="/cgi-bin/gcounter.pl?name"--> where decimal and increment are assumed
#
# Or if you don't support SSI of you don't want to use it there is another way!
# Put the following lines into your page:
#
# <table border=0 cellspacing=0 cellpadding=0><tr>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&9" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&8" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&7" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&6" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&5" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&4" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&3" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&2" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&0&1" width=15 height=18></td>
# <td><img src="http://www.yrname.com/path_to_script/gcounter.pl?name&d&1&0" width=15 height=18></td>
# </tr></table>
#
# How it works! In ?name&d&0&9 the first three values are name, format, inc as described above. The forth field is
# is added to tell what digit in the number should be displayed	
#
# The ?name&d&0&0 spot is the first decimal digit in the number. You don't need to show all ?0&d&0&0 thru ?0&d&0&9
# If your count is only 55. All that you need to show is ?0&d&0&0 and ?0&d&0&1
# But, you can show them all. If you do, it will display 0000000055
# You can go as high up to the value in $diglimit below.
#
# Also note that the the only once set to increment is the 1st digit (0), so it only increments the counter once. (not 10 times)
# The example shown above will show the counter called "name" in decimal format to 10 digits.
#
# LEGACY SUPPORT: The format of older versions of this script are still supported. ie ?name&9 where name is the counter
# 	name, 9 is the digit to be displayed, decimal format is assumed and increment is assumed on digit zero only.
#
# ALSO NOTE: If you do not use my supplied images, you will need to change the height
# and width also.
#
# chmod 777 the file & the directory, $basedir/$baseurl, specified below 
# Upload all your images to that directory too
# Upload gcounter.pl and chmod 755
#
# NOTE: If not using $onthefly counter generation mode (default yes, set below) then you must create counter files.
# If the name of your counter is 'name' then create a file called gcountname.txt in the $basedit, 
# place a 1 in it (0 will error), and chmod 777 the file
#
# Then... edit these lines...
##########################################
# This is where grahics and count files go

$basedir = "/home/maveryn/public_html/cgi-bin/counter/";
$baseurl = "http://www.maverynthia.com/cgi-bin/counter";

##########################################
# What kind of graphics files are you using?
# if you are using gif uncommnet the gif lines and
# comment the jpg lines. And visa versa.
# Set Default to the type of the graphics included.

$type = "image/gif";
$ext = ".gif";

#$type = "image/jpg";
#$ext = ".jpg";

##########################################
# What is the width and height of your graphics?
# Set Default to the size of the graphics included.
$imgwidth = "19";
$imgheight = "20";

#########################################
# Use file locking? (flock)
# set equal to yes if your server supports it

$lock = "yes";

#########################################
# Make new counters on the Fly?
# You can just put a new counter name and a
# new counter file will be made for it.
$onflynew = "no";

#########################################
# Digit Limit
# Shouldn't have to change this

$diglimit = 16;


##########################################
# NO Need to edit below here

my $use_ssi = 1;	#value to keep track if using ssi to display, default yes
my $limit;		#keeps track of how many characters are in the count
my @numstf;		#this is the count as an array with leading 0's
my $file;		#this is the file of the digit that non-ssi will display

# go and get the count,  puts it into @numstf, sets limit and file and use_ssi
&get_cnt;
# displays the count based on use_ssi, limit for ssi mode and file for non-ssi mode.. uses @numstf
&prnt_cnt;

#########################################
# Begin Subroutines
sub get_query {
	my $command;		#Query string
	my $counter_name;	#counter name parsed from query string
	my $dig2display = "";	#digit 2 display (not using ssi)
	my $format;		#number format to display (hex, bin, dec)
	my $inc;		#whether or not to inc


	if ($ENV{'QUERY_STRING'} ne '') {
		$command = "$ENV{'QUERY_STRING'}";
	} else {
		#must have command line info to operate
		error("No command line information");
	}
	
		
	if ($command =~ /(.*)&(.*)&(.*)&(.*)/) {
		$counter_name = $1;
		$format = $2;
		$inc = $3;
		$dig2display = $4;
	} elsif ($command =~ /(.*)&(.*)&(.*)/) {
		$counter_name = $1;
		$format = $2;
		$inc = $3;
	} elsif ($command =~ /(.*)&(.*)/) {
	#legacy/shortcut support for non-ssi
		$counter_name = $1;
		$format = "d";
		$dig2display = $2;
		if ($dig2display eq "0") {
			$inc = 1;
		} else {
			$inc = 0;
		}
	} else {
	#legacy/shortcut support for ssi
		$counter_name = $command;
		$format = "d";
		$inc = 1;
	}

	#error checking the query string input
	error("format is illegal") unless ($format eq "b" or $format eq "h" or $format eq "d" or $format eq "o");
	if (($dig2display) || ($dig2display eq "0")) {
		#mark the global variable that we are not using ssi
		$use_ssi = 0;
		error("digit to display is invalid") unless ($digit2display >=0 or $digit2display < $diglimit);
	}

	#return
	($counter_name,$format,$inc,$dig2display);
}


sub get_cnt {
	my $numtemp; 	#value recieved from the counter file (cannot be 0)
	my $dispnum;	#number in format to be displayed in (dec, bin, or hex)	
	my @numarray;	#turn the decimal number into a array of characters for shifting
	my $limcnt;	#counter to do the character shifting based on the limit
	my $du;		#each charater in the numarray
	my $i;		#for loop variable

	#get values from the query string
	my ($counter_name,$format,$inc,$dig2display) = &get_query;

	#see if count file exists, if not try to create it
	if (!( -e "$basedir/gcount$counter_name\.txt")) {
		if ($onflynew eq "yes") {
			&new_file($counter_name);
			$numtemp = 1;
		} else {
			error("No counter file");
		}
	} else {
		#get count
		open (CNT, "$basedir/gcount$counter_name\.txt") || error("could not get count");
		$numtemp = <CNT>;
		close(CNT);
	
		#make sure we get a number
		error("no counter number") unless ($numtemp);
	}

	#to strip of excess
	$numtemp = int($numtemp);

	#if format is not decimal, convert it to another format
	if ($format ne "d") {
		($dispnum) = num_convert($format,$numtemp);
	} else {
		$dispnum = $numtemp;
	}

	#checking for number of digits
	$limit = length($dispnum);
	$limit = $limit-1;

	#split into an array of digits
	@numarray = split(//, $dispnum);

	#initializing each to 0
	for($i=0;$i<16;$i++){
		$numstf[$i] = 0;
	}

	#filling the appropriate spots with digits
	$limcnt = $limit;
	foreach $du (@numarray) {
		$numstf[$limcnt] = $du;
		$limcnt--;
	}

	if ($inc) {
		increment_num($counter_name,$numtemp);
	}

	$file = "$basedir/$numstf[$dig2display]$ext";
	
}

sub prnt_cnt {
	my $size;	#size of the file to display in non ssi mode
	my $i;		#for loop var

	if ($use_ssi) {
		print "Content-type: text/html\n\n";
		print "<table border=0 cellpadding=0 cellspacing=0><tr>\n";
		for ($i=$limit;$i>=0;$i--){
			print "<td><img src=\"$baseurl/$numstf[$i]$ext\" width=$imgwidth height=$imgheight></td>\n";
		}
		print "</tr></table>\n";
	} else {
		if ( open(IMGF, $file)) {  
	 		$size = ( -s $file);
		 	print "Content-type: $type\n"; 
			print "Content-length: $size\n\n"; 
			while(read(IMGF,$data,1024)){ print $data; } 
			close (IMGF);
		}
	}

}


sub new_file {

	my ($counter_name) = @_;

	open (NEWCNT, ">$basedir/gcount$counter_name\.txt") || error("Could not create the new file");
	print NEWCNT "1";
	close(NEWCNT);
	
}

sub increment_num {

	my ($counter_name, $numtemp) = @_;

	$numtemp++;
	open (CNT2, ">$basedir/gcount$counter_name\.txt");

	if ($lock eq "yes") {
		flock CNT2, 2;
	}
	print CNT2 "$numtemp";
	if ($lock eq "yes") {
		flock CNT2, 8;
	}
	close(CNT2);

}

sub error {
	($error) = @_;

	print "Content-type: text/html\n\n";
	print "ERROR: $error\n";
	
	exit;
}

sub num_convert {
	my ($format, $numtemp) = @_;
	my $dectmp = $numtemp;
	my $convnum = "";
	my $rmdr;

	#format is binary
	if ($format eq "b") {
		while ($dectmp != 0) {
			$rmdr = $dectmp - (int($dectmp / 2) * 2);
			$convnum = $rmdr . $convnum;
			$dectmp = int($dectmp / 2);
		}
	#format is hexidecimal
	} elsif ($format eq "h") {
		while ($dectmp != 0) {
			$rmdr = $dectmp - (int($dectmp / 16) * 16);
			if ($rmdr eq "10") {
				$rmdr = "a";
			} elsif ($rmdr eq "11") {
				$rmdr = "b";
			} elsif ($rmdr eq "12") {
				$rmdr = "c";
			} elsif ($rmdr eq "13") {
				$rmdr = "d";
			} elsif ($rmdr eq "14") {
				$rmdr = "e";
			} elsif ($rmdr eq "15") {
				$rmdr = "f";
			}
			$convnum = $rmdr . $convnum;
			$dectmp = int($dectmp / 16);
		}
	#format is octal
	} elsif ($format eq "o") {
		while ($dectmp != 0) {
			$rmdr = $dectmp - (int($dectmp / 8) * 8);
			$convnum = $rmdr . $convnum;
			$dectmp = int($dectmp / 8);
		}
	#format is something else, so how. should never happen
	} else {
		$convnum = $numtemp;
	}

	#return
	$convnum;
}