cgi banner gif

home ch01 ch02 ch03 ch04 ch05 ch06 ch07 ch08 ch09 quick reference


Chapter 6: Adding an Access Counter

A text access counter

A graphical access counter


As mentioned in Chapter 1, CGI programs can make your Web pages dynamic, and one popular application of CGI is the addition of an access counter. An access counter is a text or graphical representation of the number of times that your Web page has been requested. Access counters work either by including a CGI script within an HTML page with Server Side Includes or the <IMG> tag, or by generating the entire HTML page from a CGI script. When a user requests the HTML page containing an access counter, the CGI script checks the current access count, increments it by one, and displays the results in the Web page. Many Web sites have added access counters to their home pages. Figure 6.1 displays some ways that access counters can be displayed.

Figure 6.1: Acccess counters

As shown in the figure, access counters can be displayed as text or as an image file representing the value of the counter. As you might imagine, the graphical version requires more work in the CGI script than simply displaying the value of the counter as text. To display the graphic, you must either have a graphic file for every possible number the counter can reach or you must construct the graphical image from several image files. If you use the latter method, you only need to store 10 files, one for each digit 0 through 9. Then, when necessary, your CGI script creates the image file by putting together the separate GIF graphic files into a single GIF that the browser displays. For example, when the access counter is at 986, the CGI script takes the individual GIF files of the digits 9, 8, and 6 and combines them into one GIF that is the combination of all three. GIF image files are just one of many image file formats. Two other types of image file types that you can work with are JPEG and XBM images, because many Web browsers can display them. However, this chapter uses GIF image files, as you'll see in the section "A Graphical Access Counter" later in this chapter.

This chapter starts with an example text access counter. You should read this section even if you want to create a graphical access counter, because the basic functionality of the two types of counters is the same. The second half of the chapter explains how to create a graphical access counter. In this case, the process is a bit more challenging, but the results are a lot more impressive.


A text access counter

The text access counter displays the count number in plain text within the HTML page. Figure 6.1 displayed several text and graphical access counters. Because the text counter uses plain text, it is much easier to implement than a graphical counter. The biggest decision is how to call the access counter script. It's easiest to do this with Server Side Includes, but not all systems have Server Side Includes enabled. The following sections describe how to create the text access counter script and explain two different ways of adding it to your Web pages.

Creating the Counter Script

Before starting your CGI script, you should spend a moment thinking about what it must do. The script needs access to the current value of the access counter. For this information to be available, you must store it in a text file. Every time your text access counter script is called, it opens the counter file in which the current access count is stored, increments the access count, and saves the new number in the counter file. After the counter value has been incremented, the number is included in the Web page.

The Increment subroutine

Your text access counter first needs to read in the current access count, which is stored in the counter file. In this chapter, the text file containing the access count is named count.dat. You can read in the value stored in count.dat simply by opening the file and reading the first line into the variable $count,, as in the following lines of Perl code:

open(COUNT, "$file") || die "Content-type: text/html\n\nCannot open counter 
file!";

$count = <COUNT>; close(COUNT);


The first line opens the file whose name is stored in the variable $file. You set this variable when you put together the entire text access counter script shown in Listing 6.2. For now, just note that $file will contain the path and name of the file that stores the current number of accesses to your Web page. The second part of the first line of Perl code contains a die statement that will terminate the program and output the contents of the string:

Content-type: text/html\n\nCannot open counter file!

The || operator between the open and die statements is the logical OR operator. When this operator is between the two statements, the Perl interpreter first tries to execute the open statement. If the open is successful, the Perl interpreter moves on to the next line of code. However, if the file cannot be opened, the Perl interpreter executes the die statement. This is a common way to verify that a file is successfully opened and to terminate the Perl program if it is not.

The second line of code reads in the contents of the first line of the counter file from the input stream <COUNT> and places it into the variable $count. After the line has been read in from the file, you can close the input stream <COUNT> by using the close command, as in the third line of code.

Now that you have the code to get the current value of the access counter, you need to increment the value and write the new value to the counter file. You can easily increment the value by using the ++ operator. If you append this operator to a variable name, the integer value stored in the variable is increased by 1. For example, if the current value of the access counter were 2, the Perl code


$count++;

would change the value to 3.

Once the access count has been incremented, you need to store the new value in the counter file for the next time the script is called. The following three lines of Perl code open the counter file and write the new access count to the file:


open(COUNT, ">$file") || die "Content-type: text/html\n\nCannot open counter file!";
print COUNT $count;
close(COUNT);

Again, you use the open statement to open the counter file. However, this time you output to the file instead of receiving input from the file. Notice the > operator before the $file variable in the open statement. This operator opens the file for output. If the file already exists, it is overwritten. The second line prints the contents of the $count variable into the file.

You now have all the code necessary to read in the current access count, increment it, and write it back to the file. To make your code easier to read, place it within a subroutine called Increment. Listing 6.1 shows the contents of the Increment subroutine.

Listing 6.1: The Increment Subroutine
sub Increment {
  local ($count);

  # Get the current value of the access counter.
  open(COUNT, "$file") || die "Content-type: text/html\n\nCannot open 
counter file!";
  $count = <COUNT>;
  close(COUNT);

  # Increment the access counter

  $count++;

  # Store the value of the counter in the counter file.
  open(COUNT, ">$file") || die "Content-type: text/html\n\nCannot open 
counter file!";
  print COUNT $count;
  close(COUNT);

  return $count;
}



Besides the sub Increment line, which declares the subroutine, the only lines of code that have been added are the local statement at the beginning and the return statement at the end. The local statement,

local ($count);

declares that the $count variable is local only to the Increment subroutine. A local variable is a variable that exists only within a portion of your Perl code, usually within a subroutine. If a variable with the same name existed outside the subroutine, Perl would consider it a different variable than the one that is declared local within the subroutine. Declaring your subroutine's variables as local helps to keep your subroutines from overwriting values of global variables. A global variable is one that is accessible throughout the entire Perl program, including any subroutines in the same Perl file. In Listing 6.1, the variable $file is a global variable. Listing 6.2 in the next section will show where this variable is set.

The other line of Perl code added to your Increment subroutine is the return statement


return $count;

This statement causes the subroutine to return the current value of the count as its return value. In Perl, every subroutine returns a value. You can set this return value explicitly by using the return statement. The next section demonstrates how this return value is used.

The complete text access counter script

Now that it can increment the counter, your script just needs to return the value of the counter for display in the Web page. You can do this by using the following three lines of Perl:

$access_number = &Increment;
print "Content-type: text/html\n\n";
print $access_number;

The first line calls the Increment subroutine and assigns the return value to the variable $access_number. The next line prints the required parsed header. The last line prints the value of the access counter.

You can place the preceding lines of Perl code, along with the code for the Increment subroutine, in a file called access.pl. Listing 6.2 shows the complete access.pl file. Notice that the global variable $file is set at the beginning of the file and is used within the Increment subroutine. Also, if you use this counter on a machine running a version of Windows, remove the first line and change the path for the count.dat file.

Listing 6.2: The access.pl file
#!/usr/local/bin/perl

# All users need to change the value of this
# variable to the path for their machine. Windows
# users need to use a format similar to
# "c:\\robertm\\count.dat"
$file = "/users/robertm/count.dat";

$access_number = &Increment;
print "Content-type: text/html\n\n";
print $access_number;

sub Increment {
  local ($count);

  # Get the current value of the access counter.
  open(COUNT, "$file") || die "Content-type: text/html\n\nCannot open 
  counter file!";
  $count = <COUNT>;
  close(COUNT);

  # Increment the access counter
;
  $count++;

  # Store the value of the counter in the counter file.
  open(COUNT, ">$file") || die "Content-type: text/html\n\nCannot open 
  counter file!";
  print COUNT $count;
  close(COUNT);

  return $count;
}



Seeding the Counter

Before you can use your access counter, you need to supply the file count.dat with the initial count value. This is called seeding the counter. Normally, you can simply create a text file with the number 0 on the first line. This will start your counter at zero. The first person visiting your Web page with the counter would see the access number 1. (For the graphical counter script, you need to seed the count.dat file with the number 1 for the first person visiting your Web page to see the access number 1. You'll learn why in the section "A Graphical Access Counter" later in this chapter.) If you want to start your counter at a higher number, just change the first line of the count.dat file to that number. For example, if your Web page has already been accessed 342 times, create a text file named count.dat with the number 342 on the first line (use 343 for the graphical counter).

Adding the Counter to Your HTML Page

With the access.pl and count.dat files completed, you are now ready to add the counter to your Web page. The easiest way to do so is to use a Server Side Include. However, after reading about security issues in Chapter 3 you may have disabled Server Side Includes on your system. Or you may be using a Web server on which the administrator has turned off Server Side Includes and does not wish to re-enable them. For these reasons, this section also describes an alternative way of adding your text access counter that doesn't require a Server Side Include. This technique takes more work to implement, however, because you have to make changes to the access.pl script.

With Server Side Includes

As mentioned, it's easiest to implement your text access counter using Server Side Includes. Simply choose which Web page you want to display your counter and add some surrounding text and the following Server Side Include:

<!--#exec cgi="/cgi-bin/access.pl" -->

If Server Side Includes are enabled on your Web server, the Web server will parse this line before sending it to the user's Web browser. The CGI script access.pl is executed and the output from the script is substituted for the preceding line in the HTML.

For example, the following HTML code demonstrates how to add the Server Side Include statement to your existing HTML page:


<HTML>
<HEAD>
<TITLE>Example of Text Access Counter</TITLE>
</HEAD>
<BODY>
<H1>Text Access Counter</H1>
This is my Home page. Thank you for visiting. Please come again.
<P>This page has been accessed
<!--#exec cgi="/cgi-bin/access.pl" -->
 times.
</BODY>
</HTML>

As you can see, the Server Side Include was added along with some surrounding text to explain what the number represents. The surrounding text is "This page has been accessed x times." The x represents where the Web server would insert the value returned by your access.pl script. Figure 6.2 shows how the HTML page will appear when the access count is 13.

Figure 6.2: The text access counter

Without Server Side Includes

Suppose you want to use the text access counter without using a Server Side Include. It is still possible, but you have to generate the entire page from your CGI script. You can change your HTML page to a template and have your CGI script read in the template, place the current value of the counter within the template, and output the entire page to the user's Web browser.

Creating the Template File

The first thing to do is to change the HTML file, which you want to contain the counter, into a template file. Do this by changing the file's extension to .tmpl (.tmp if you want to use a three-character extension). Then edit the file, inserting the text that will surround the counter value. Use the value XXXX as a placeholder for where you will insert the access count. For example, using the HTML from the preceding example, you would place the HTML code in a file called text.tmpl, whose contents are shown here:

<HTML>
<HEAD>
<TITLE>Example of Text Access Counter</TITLE>
</HEAD>
<BODY>
<H1>Text Access Counter</H1>
This is my Home page. Thank you for visiting. Please come again.
<P>This page has been accessed XXXX times.
</BODY>
</HTML>

Modifying the access.pl Script

After you have created the template file, you must modify your access.pl script to open the template file, read in the contents, change the XXXX placeholder into the current access count, and output the HTML code to the Web browser. It is easiest to add this code in a subroutine called Display_Page and simply call the subroutine after the counter has been incremented. Listing 6.3 contains the Perl code for this subroutine.

Listing 6.3: The Display_Page Subroutine
sub Display_Page {
  local ($count) = @_;

  # All users need to change the following path
  # to be correct for their system. Windows users
  # need it in the form "c:\\robertm\\text.tmpl".
  local ($html) = "/users/robertm/text.tmpl";
  local (@template);

  open(TEMPLATE, "$html") || die "Content-type: text/html\n\nCannot 
  open template!";
  @template = <TEMPLATE>;
  close(TEMPLATE);

  # Be sure to change the index number to the correct one
  # for your HTML template. Count down from the first line
  # being at Ø to the line containing the XXXX placeholder.
  $template[7] =~ s/XXXX/$count/e;

  print "Content-type: text/html\n\n";
  print @template;
  
}



The Display_Page subroutine begins by declaring the two variables and one array that will be local to this subroutine. The variable $html stores the name and path to the template file that the access counter will be added to. Then the template file is opened and all of its contents are read into the @template array. In this example, the placeholder for the access number XXXX is on the eighth line of text.tmpl file. When the file is read into the array, the eighth line is placed in the eighth element of the @template array. Because the index of Perl arrays begins with 0, the eighth element is at index 7. So, the statement

$template[7] =~ s/XXXX/$count/e;

takes the eighth element of the @template array and replaces the access number placeholder with the current count. If you use this subroutine for your HTML template, you need to change the index of the @template array element to the correct index for your template file.

After the placeholder for the access count is changed to the actual count number, the required parsed header and the contents of the @template array are returned to the user's Web browser. Because this entire page is being generated from the CGI script, you need to change all the links to this page to call your CGI script, access.pl, which should now be modified to contain the new Display_Page subroutine. Listing 6.4 is the Perl code for the new access.pl script. Notice how the two lines that previously output the parsed header and the access count have been replaced with a call to the Display_Page subroutine. Don't forget to modify the paths for the $file and $html variables to contain the correct paths for your system. Also, Windows users must remember to remove the first line of the script file. Figure 6.3 shows how Netscape displays a call to the modified access.pl script when the counter value is 13. Notice that the page looks the same as the one in Figure 6.2, which is called with a Server Side Include.

Listing 6.4: The modified access.pl script
#!/usr/local/bin/perl

# All users need to change the value of this
# variable to the path for their machine. Windows
# users need to use a format similar to
# "c:\\robertm\\count.dat"
$file = "/users/robertm/count.dat";

$access_number = &Increment;
&Display_Page($access_number);

sub Increment {
  local ($count);

  # Get the current value of the access counter.
  open(COUNT, "$file") || die "Content-type: text/html\n\nCannot open 
  counter file!";
  $count = <COUNT>;
  close(COUNT);

  # Increment the access counter

  $count++;

  # Store the value of the counter in the counter file.
  open(COUNT, ">$file") || die "Content-type: text/html\n\nCannot open 
  counter file!";
  print COUNT $count;
  close(COUNT);

  return $count;
}

sub Display_Page {
  local ($count) = @_;

  # All users need to change the following path
  # to be correct for their system. Windows users
  # need it in the form "c:\\robertm\\text.tmpl".
  local ($html) = "/users/robertm/text.tmpl";
  local (@template);

  open(TEMPLATE, "$html") || die "Content-type: text/html\n\nCannot 
  open template!";
  @template = <TEMPLATE>;
  close(TEMPLATE);

  # Be sure to change the index number to the correct one
  # for your HTML template. Count down from the first line
  # being at Ø to the line containing the XXXX placeholder.
  $template[7] =~ s/XXXX/$count/e;

  print "Content-type: text/html\n\n";
  print @template;
  
}



Figure 6.3: The results of the modified access.pl


A graphical access counter

The graphical access counter presents more challenges than the text counter. The graphical counter has the same basic functionality as the text access counter, such as reading in the counter value from the counter file, incrementing the counter value, and saving the value in the counter file. However, it must also create a graphical image that is a representation of the access count. For example, if the access count were 293, the graphical access counter would return an image containing the number 293.

The easiest way to accomplish this is to have a GIF file for every possible number the access counter can reach. For example, if the counter value were 769, your graphical counter script would only have to return the GIF file containing the number 769. However, doing the counter this way wastes a great deal of disk space. If your access counter only goes up to 1,000, you have to create and save 1,000 different GIF files on your Web server. Even if these files are 1K or less apiece, this still amounts to nearly 1 megabyte in disk storage. Maybe 1 megabyte doesn't seem like that big of a deal to you, but keep in mind that 1,000 accesses on the World Wide Web is a very small number. Realistically, if you have a moderately popular site, you want your counter to reach at least 100,000. It would take a tremendous amount of disk space and a lot of time to create files for this many numbers.

The more difficult method for returning a graphical image for the counter value is to create the graphical image dynamically from ten existing GIF files for the digits 0 through 9. Your CGI script would construct the graphical counter image from these digits by combining the GIF files in a new image. For example, if the counter value were 769, your CGI script would take the digit file for the 7, append the digit file for the 6, and then append the digit file for the 9, creating a GIF image representing the number 769.

As you can imagine, this approach is much more difficult than the text access counter example. To write the code for this GIF creation, you need to know quite a bit about the GIF file format and must be able to write routines that create a single GIF image from multiple existing GIF files. Although you could probably learn how to do this, there is a easier way.

Recall from Chapter 3 that one of the reasons to use a common language for CGI scripting is to be able to use library routines written by other people. Well, now is a perfect time to take advantage of one such library. Thomas Boutell has written a graphics library of functions in C that handle GIF creation and manipulation. This library, called gd, is available from http://www.boutell.com/gd/. His only restriction on the use of the gd library is that the credit and copyright given to the Quest Protein Database Center, Cold Spring Harbor Labs remain intact in all derived works. In other words, if you distribute the gd graphics library files with your code you cannot remove the copyright lines in the gd files. Although Lincoln Stein has written a Perl interface to the gd graphics library called GD.pm, the following graphical access counter does not use GD.pm. To use GD.pm, you must still download and install the gd graphics library, and GD.pm may not be easily installed on all systems. For this reason, the C programming language is used to create the graphical counter script for this graphical access counter example.

Before you start creating the graphical counter script, you should download and install the gd graphics library. You can download the source files from Thomas Boutell's Web site, http://www.boutell.com/gd/. Once you have downloaded and uncompressed the source files for gd, you should find a README file that contains directions for installing the graphics library.

Most of the code for the graphical access counter script is logically similar to the code for the text access counter. There is one major difference, however. In the text access counter example, the counter file contained the current count. When the text access counter script is called, it increments the counter value before displaying it in the Web page. The graphical counter, in contrast, stores the next counter value in the counter file instead of the current counter value. When the graphical counter script is called, it displays the value read from the file as the current count, not the incremented value. This makes it easier to create the GIF image, as explained in more detail in the section "Creating the GIF Image."

Incrementing the Counter

Incrementing the graphical access counter is similar to incrementing the text access counter. However, the C code for the graphical counter looks dramatically different than the Perl code for the text access counter. Listing 6.5 contains the C code that reads in the counter value from the counter file, increments the value, and writes the new counter value to the counter file.

Listing 6.5: The C code for incrementing the counter
/* Counter file handle */
FILE *counter;

/* string version of counter file value */
  char s_count[2Ø]; 

/* Counter file 
    All users need to change the path to the actual 
    path for their machines. Windows users need to
    use a path in the form "c:\\robertm\\count.dat" */
char *cnt_file = "/users/robertm/count.dat";

  /* integer version of count */
int count;

/* open the counter file */
counter = fopen(cnt_file, "r");

/* check whether the file was opened successfully */
if (!counter) {
  printf("Content-type: text/html\n\nCannot open counter file!\n");
  return(Ø);
} else {
  /* if the file was opened, read in the string containing the count */
  fgets(s_count, 2Ø, counter); 
  fclose(counter);

  /* extract the integer value of the count from the string */
  sscanf(s_count, "%d", &count);

  /* increment the count */
  count++;

  /* open the count file for output */
  counter = fopen(cnt_file, "w");

  /* check whether the file was opened successfully */
  if (!counter) {
    printf("Content-type: text/html\n\nCannot open counter file!\n");
    return(Ø);
  } else {
    /* if the file was opened, write the count value to it */
    fprintf(counter, "%d", count);
    fclose(counter);
  }
}



The first several lines define the variables that will be used in this code. After the variable declarations, the counter file is opened with the line

counter = fopen(cnt_file, "r");

This line opens an input stream, named counter, to the file whose name and path are in the cnt_file variable. The "r" string designates that the file will be opened for input only. It is always a good idea to verify whether the file has been successfully opened. The line

if (!counter) {

does this by making sure that the counter holds a nonzero value. If the file cannot be opened, the counter variable is set to 0. If counter is equal to 0, the first part of the if...else statement executes. In this section, you output an error message that the file could not be opened.

Under the else portion of the if...else, the access counter value is read in from the file with the line


fgets(s_count, 2Ø, counter);

This statement reads up to 20 characters from the file pointed to by the counter variable and places the characters in the s_count string. (Actually, s_count is an array of characters, which is the C equivalent of a string.)

After the access counter value is read from the file, the stream to the file is closed. The next line


sscanf(s_count, "%d", &count);

converts the counter value from a string to an integer and stores the integer value in the count variable, which can then be incremented with the

count++;

statement.

Finally, the counter file is once more opened, and the incremented value of the access count is written to the file. Notice how the open statement


 counter = fopen(cnt_file, "w");

has the string "w" rather than the string "r". The "w" means to open the file for output. The value of the access counter is written to the file with the line

fprintf(counter, "%d", count);

which prints the decimal value of the count variable into the file pointed to by the counter variable, which is the access counter file.

Creating the GIF Image

Once the counter value has been read in from the count.dat file, the GIF image can be created. Recall that you create the GIF image from the counter value you read in from the file, not from the incremented value. This is because the incremented version is stored in an integer, and it is easier to create the GIF based on a value stored in a string. Remember, a string in C is actually an array of characters. So, if the counter value is 769, the s_count variable contains the string 769. Each individual digit can easily be accessed by addressing the specific array element. For example, the first digit, 7, can be accessed by using the variable s_count[0], which is the first element in the s_count array. In this way, you can loop over each digit in the counter value and append the corresponding GIF image file to the GIF image you are creating. With the 769 example, you would first add the contents of the 7 GIF image file, next the contents of the 6 GIF image file, and then the contents of the 9 GIF image file.

Before looping over the s_count array and creating the new GIF image, you must either create or download the ten GIF images representing each digit. For this example, the ten GIF images have been downloaded from the Digit Mania Web site, http://cervantes.learningco.com/kevin/digits/index.html. This Web site contains many digit styles for use in graphical access counter programs. The style used here is the tiny style, which was created by Muhammad A. Muquit. Each one of these GIF digits is a 9-pixel wide by 13-pixel high image. To implement this example exactly as shown, you need to download these images and place them in a directory that your graphical counter CGI script can access.

Now that you have the ten GIF images, one for each digit, you can write the C code for creating the graphical counter image that will be displayed within your Web page. First you must create a blank version of the graphical counter image that is the same width and height as your final image. By doing this, you allocate the memory necessary to store the final graphical counter image and you create the image to which the individual digit images can be copied. Because the digit images will each be 13 pixels high, your final image will also be 13 pixels high. However, the width must be calculated based on the number of digits the number contains. Remember that the length of the s_count array is also the number of digits your graphical counter image will contain. Therefore, you can calculate the width of your final image by multiplying the length of the s_count array by the width of each individual digit image, which in this case is 9 pixels. You can do this with the code


width = strlen(s_count) * real_width;

where real_width is an integer that is set to 9 earlier in the program. Listings 6.6 and 6.7 later in this chapter show where the real_width variable is set.

After you have calculated the dimensions of the graphical counter image, you can use the gdImageCreate function from the gd library to create the new graphical counter image. This function takes two parameters, a width and height, and creates a blank GIF image with the specified dimensions. The C statement


im_final = gdImageCreate(width, real_height);

calls the gdImageCreate function, passing it the width value that was just calculated and the height of the individual digit images, which is stored in the variable real_height. Listings 6.7 and 6.8 will show the code that sets the real_height variable to 13, which is the height for the tiny style digit images. Notice that the return value of the function gdImageCreate is assigned to the variable im_final. This variable is a pointer to the image that was just created, and it is through this variable that you will reference the image later on in the code.

Next you need to loop over the array s_count and append the digit image files to the im_final image you just created. The loop you use will be similar to the for loops used in Perl. The line


for (i=Ø; i < strlen(s_count); i++) { 

starts the for loop. The loop executes once for every element in the s_count array and the variable i represents the index of the current s_count array element. For example, if s_count holds the string 769, the for loop executes three times. The first time, the i variable is 0, so the first digit of the s_count array can be accessed by using the variable s_count[i]. As i is incremented, the variable s_count[i] moves to the next elements in the array. For each iteration of the loop, you should check the value of the s_count[i] array element, open the digit image file that corresponds to that value, and append the contents of that digit's GIF image file to the graphical counter image you are creating. Listing 6.6 shows the C code for the entire for loop.

Listing 6.6: The graphical counter for loop
for (i=Ø; i < strlen(s_count); i++) { 
  switch (s_count[i]) {
    case 'Ø':
      image = fopen(zero_gif, "rb");
      break;
    case '1':
      image = fopen(one_gif, "rb");
      break;
    case '2':
      image = fopen(two_gif, "rb");
      break;
    case '3':
      image = fopen(three_gif, "rb");
      break;
    case '4':
      image = fopen(four_gif, "rb");
      break;
    case '5':
      image = fopen(five_gif, "rb");
      break;
    case '6':
      image = fopen(six_gif, "rb");
      break;
    case '7':
      image = fopen(seven_gif, "rb");
      break;
    case '8':
      image = fopen(eight_gif, "rb");
      break;
    case '9':
      image = fopen(nine_gif, "rb");
      break;
    default :
      break;
  }

  if (!image) {
    printf("Content-type: text/html\n\nCannot open image file!\n");
    return(Ø);
  } else {
    im_file = gdImageCreateFromGif(image);
    fclose(image);
    gdImageCopy(im_final, im_file,
                  destx, Ø, Ø, Ø, real_width, real_height);
    destx += real_width;
    gdImageDestroy(im_file);
  }
}



The first section of the for loop uses the switch conditional, which compares the value of the s_count[i] array element with each case until a match is found. When a matching case is found, the code under the case is executed. If no match is found, the code under the default is executed. For example, if the s_count array contains 769, the first iteration of the loop would have the s_count[i] element equal to 7. The switch would execute the code under the case '7': line, which is

image = fopen(seven_gif, "rb");
break;

The first line opens the 7 digit's GIF image file, whose path and file name are stored in the seven_gif variable. The code setting this variable is shown in Listings 6.6 and 6.7. The difference between this fopen statement, and the ones in the previous section is the "rb" string. This string specifies to open the file for binary input.

At the end of the switch statement, an if...else conditional verifies that the image file has been successfully opened. In the else portion of the conditional, the digit image is appended to the graphical counter image being created. This is done with the following lines of code:


im_file = gdImageCreateFromGif(image);
fclose(image);
gdImageCopy(im_final, im_file,
                  destx, Ø, Ø, Ø, real_width, real_height);
destx += real_width;
gdImageDestroy(im_file);

The first line creates a new gd graphical image from the digit's GIF image file. The pointer im_file points to this image. After the im_file image has been created, you can close the stream to the file with the fclose statement. The statement

gdImageCopy(im_final, im_file,
                  destx, Ø, Ø, Ø, real_width, real_height);

appends the digit's image just read in from the image file to the graphical counter image that is being constructed. The parameters for gdImageCopy are the following: a pointer to the destination image, a pointer to the source image, the x coordinate in the destination image to start placing the copy, the y coordinate in the destination image to start placing the copy, the x coordinate in the source image to begin the copy, the y coordinate in the source image to begin the copy, the number of pixels to copy in the x direction, the number of pixels to copy in the y direction. In this statement, the destination image is the graphical counter image you are creating, which is pointed to with the im_final variable. The source image is the image that was just read in from the digit's GIF file, and is pointed to with the im_file. The x coordinate for the destination of the copy is a variable, destx. This variable is increased after each iteration of the loop, in the line

destx += real_width;

For example, with the s_count array containing 769, the first iteration copies the 7 digit's GIF image file to the im_final image. Because this is the first image being copied, the x coordinate in the destination image is 0. However, on the next iteration of the loop, the im_final image already contains the 7 digit. So, the x coordinate has to be moved over 9 pixels. Why 9 pixels? Because that is the width of each individual digit's GIF image. After the 6 digit's image file is added to the im_final image, the destx variable is increased by another 9 pixels. So, when the 9 digit is added to the im_final image, the destx variable is 18, which is the sum of the widths of the previous two digits 7 and 6. Finally, after the im_file image is appended to the im_final image, the im_file image is destroyed with the gdImageDestroy command. Listing 6.7 shows the all of the C code for creating the graphical counter image.

Listing 6.7: The C code for creating the graphical counter image
/* Image file handle */
FILE *image;

/* Input and output images */
gdImagePtr im_file, im_final;

/* Image files 
      All users need to change the paths to the actual 
      paths for their machines. Windows users need to
      use paths in the form "c:\\robertm\\Øtiny.gif" */
char *zero_gif = "/users/robertm/0tiny.gif";
char *one_gif = "/users/robertm/1tiny.gif";
char *two_gif = "/users/robertm/2tiny.gif";
char *three_gif = "/users/robertm/3tiny.gif";
char *four_gif = "/users/robertm/4tiny.gif";
char *five_gif = "/users/robertm/5tiny.gif";
char *six_gif = "/users/robertm/6tiny.gif";
char *seven_gif = "/users/robertm/7tiny.gif";
char *eight_gif = "/users/robertm/8tiny.gif";
char *nine_gif = "/users/robertm/9tiny.gif";

/* string version of counter file value */
char s_count[2Ø]; 

/* width of image created image, 
   x position for new image, and loop variable */
int width;
int destx = Ø;
int i;

/* the real_height is the height (in pixels) for all of the image files.
   the real_width is the width (in pixels) for all of the image files. */
int real_height = 13;
int real_width = 9;

width = strlen(s_count) * real_width;
im_final = gdImageCreate(width, real_height);

for (i=0; i < strlen(s_count); i++) { 
  switch (s_count[i]) {
    case 'Ø':
      image = fopen(zero_gif, "rb");
      break;
    case '1':
      image = fopen(one_gif, "rb");
      break;
    case '2':
      image = fopen(two_gif, "rb");
      break;
    case '3':
      image = fopen(three_gif, "rb");
      break;
    case '4':
      image = fopen(four_gif, "rb");
      break;
    case '5':
      image = fopen(five_gif, "rb");
      break;
    case '6':
      image = fopen(six_gif, "rb");
      break;
    case '7':
      image = fopen(seven_gif, "rb");
      break;
    case '8':
      image = fopen(eight_gif, "rb");
      break;
    case '9':
      image = fopen(nine_gif, "rb");
      break;
    default :
      break;
  }

  if (!image) {
    printf("Content-type: text/html\n\nCannot open image file!\n");
    return(0);
  } else {
    im_file = gdImageCreateFromGif(image);
    fclose(image);
    gdImageCopy(im_final, im_file,
                  destx, Ø, Ø, Ø, real_width, real_height);
    destx += real_width;
    gdImageDestroy(im_file);
  }
}



Returning the Graphical Counter Image

Now that the counter value is incremented and saved to the file and the graphical counter image is created, you are ready to return the graphical counter image from your CGI script. Just before returning the image, you can call the gd library function

gdImageInterlace(im_final, 1);

which interlaces the im_final image. Interlacing displays the GIF image incrementally as it is downloaded in certain Web browsers that support interlaced GIFs. An interlaced GIF first appears somewhat distorted and then gradually become more clear as the remainder of the file is downloaded across the Internet. When you use interlaced GIF images, the users viewing your Web pages see an image more quickly, even if it's a little distorted. This makes it appear as though the whole image is downloading more quickly. Because the graphical counter image is not very large, interlacing is not imperative. However, if you used one of the larger digit styles available from the Digit Mania Web page, you would probably want to interlace your image.

Before outputting the graphical counter image, you must return a parsed header, as you did for the text access counter. However, the parsed header for the graphical image counter needs to specify that the data being returned is a GIF image. The following lines output the parsed header and the graphical counter image.


printf("Content-type: image/gif\n\n");
gdImageGif(im_final, stdout);

With the graphical counter image returned for inclusion in your Web page, you can now destroy the im_final image with the statement

gdImageDestroy(im_final);

You should always call the gdImageDestroy function when you are finished with a gd Image because it frees up any memory that was allocated for that image.

The Graphical Counter Script

In the previous two sections, you developed all of the code for the graphical counter script. Listing 6.8 puts all of the code together and places the necessary #include statements at the beginning. You need to make the specified changes to the variable assignments that assign the path and file names of the counter and digit image files. Because the graphical counter script is written in C, you also need to compile the code before you run it. The procedure for doing so varies depending on what system you are on and which C compiler you have. Using your C compiler, compile the graphical-counter.c file and create the graphical-counter executable file. (If your system restricts you to an eight-character file name, call the file gph-cnt.c and the executable gph-cnt.)

Listing 6.8: The graphical-counter.c File
#include <stdio.h>
#include <string.h>
#include "gd.h"

int main(void) 
{
  /* Counter file handle and image file handle */
  FILE *counter;
  FILE *image;

  /* Input and output images */
  gdImagePtr im_file, im_final;

  /* Counter file and image files 
      All users need to change the paths to the actual 
      paths for their machines. Windows users need to
      use paths in the form "c:\\robertm\\count.dat" */
  char *cnt_file = "/users/robertm/count.dat";
  char *zero_gif = "/users/robertm/Øtiny.gif";
  char *one_gif = "/users/robertm/1tiny.gif";
  char *two_gif = "/users/robertm/2tiny.gif";
  char *three_gif = "/users/robertm/3tiny.gif";
  char *four_gif = "/users/robertm/4tiny.gif";
  char *five_gif = "/users/robertm/5tiny.gif";
  char *six_gif = "/users/robertm/6tiny.gif";
  char *seven_gif = "/users/robertm/7tiny.gif";
  char *eight_gif = "/users/robertm/8tiny.gif";
  char *nine_gif = "/users/robertm/9tiny.gif";

  /* string version of counter file value */
  char s_count[2Ø]; 

  /* integer version of count, width of image, 
     x position for new image, and loop variable */
  int count;
  int width;
  int destx = Ø;
  int i;

  /* the real_height is the height (in pixels) for all of the image files.
     the real_width is the width (in pixels) for all of the image files.
     Note: If you want to use different digit image files, replace these
     numbers with the width and height (in pixels) of the digit image
     files you will be using. */
  int real_height = 13;
  int real_width = 9;

  /* open the counter file */
  counter = fopen(cnt_file, "r");

  /* check whether the file was opened successfully */
  if (!counter) {
    printf("Content-type: text/html\n\nCannot open counter file!\n");
    return(Ø);
  } else {
    /* if the file was opened, read in the string containing the count */
    fgets(s_count, 2Ø, counter); 
    fclose(counter);

    /* extract the integer value of the count from the string */
    sscanf(s_count, "%d", &count);

    /* increment the count */
    count++;

    /* open the count file for output */
    counter = fopen(cnt_file, "w");

    /* check whether the file was opened successfully */
    if (!counter) {
      printf("Content-type: text/html\n\nCannot open counter file!\n");
      return(Ø);
    } else {
      /* if the file was opened, write the count value to it */
      fprintf(counter, "%d", count);
      fclose(counter);
    }
  }

  width = strlen(s_count) * real_width;
  im_final = gdImageCreate(width, real_height);

  for (i=Ø; i < strlen(s_count); i++) { 
    switch (s_count[i]) {
      case 'Ø':
        image = fopen(zero_gif, "rb");
        break;
      case '1':
        image = fopen(one_gif, "rb");
        break;
      case '2':
        image = fopen(two_gif, "rb");
        break;
      case '3':
        image = fopen(three_gif, "rb");
        break;
      case '4':
        image = fopen(four_gif, "rb");
        break;
      case '5':
        image = fopen(five_gif, "rb");
        break;
      case '6':
        image = fopen(six_gif, "rb");
        break;
      case '7':
        image = fopen(seven_gif, "rb");
        break;
      case '8':
        image = fopen(eight_gif, "rb");
        break;
      case '9':
        image = fopen(nine_gif, "rb");
        break;
      default :
        break;
    }

    if (!image) {
      printf("Content-type: text/html\n\nCannot open image file!\n");
      return(Ø);
    } else {
      im_file = gdImageCreateFromGif(image);
      fclose(image);
      gdImageCopy(im_final, im_file,
                    destx, Ø, Ø, Ø, real_width, real_height);
      destx += real_width;
      gdImageDestroy(im_file);
    }
  }

  gdImageInterlace(im_final, 1);

  printf("Content-type: image/gif\n\n");
  gdImageGif(im_final, stdout);
  gdImageDestroy(im_final);
}



Calling the Counter with the <IMG> Tag

With the graphical counter script completed, you are ready to add the graphical access counter to your Web page. As with the text access counter, you need to create and seed the count.dat file. If you didn't follow the text access counter example, read the section "Seeding the Counter" earlier in this chapter. The graphical access counter is called from the <IMG> HTML tag. The line

<IMG SRC="/cgi-bin/graphical-counter">

calls the graphical-counter file and displays the results returned from the script. Listing 6.9 contains the HTML code for an example using the graphical counter script, and Figure 6.4 shows how the page would appear in the Netscape browser.

Listing 6.9: HTML Example Using the Graphical Counter
<HTML>
<HEAD>
<TITLE>Example of the Graphical Access Counter</TITLE>
</HEAD>
<BODY>
<H1>Graphical Access Counter</H1>
This is my Home page. Thank you for visiting. Please come again.
<P>This page has been accessed <IMG SRC="/cgi-bin/graphical-counter"> 
times.
</BODY>
</HTML>



Figure 6.4: The graphical access counter


home ch01 ch02 ch03 ch04 ch05 ch06 ch07 ch08 ch09 quick reference