HTML & CGI Unleashed

Chapter 32. A Graphical Web Page Counter

by Kelly Black


Book Information
Title: HTML & CGI Unleashed Book Support Web
URL: http://www.december.com/works/wdg.html
Category: Internet/Web Related
Keywords: Web development, World Wide Web, Book, Documentation
Contact Name: John December
Contact Email: john@december.com
Description: HTML & CGI Unleashed covers complete life cycle of web development: planning, analysis, design, and HTML implementation and gateway programming using Perl, REXX, and C.
Book Data: John December and Mark Ginsburg: HTML & CGI Unleashed. Sams.Net Publishing (Phone: 1(800)428-5331; Direct calls 1-317-581-3547, Indianapolis Indiana, 1995. 830 pages + CD-ROM, ISBN 0-672-30745-6, US$49.99




Chapter 32

Outline

Section 1. Introduction

A page counter is a simple script that runs each time a page is accessed and updates a data file. Each time a page is viewed the script must read in a count from a data file and increment the count. The count is then displayed (see Figure 4). Ideally, it would be easiest to call a program from within an HTML document. Since this won't become practical until the HTML 3.0 standard is agreed upon, the question then becomes how to circumvent this restriction. The answer comes from the way images are viewed within an HTML document. By using the IMG tag an image can be easily viewed. The source field within the IMG tag can specify any URL that can be read as a graphic image. Using the IMG tag, I will examine two different programs which can generate a graphics image. The image will simply be the number of times a page has been accessed.

Before the two methods are given a simpler example script, written in c, is examined. The basic idea is to open a data file specified as an argument, read in a number from the file, increment the number, and write the new number to the file. Given the number read from the file, the script will print out the number as plain text that any WWW browser can read. Ironically, graphics-based browsers such as Mosaic and Netscape cannot process plain text within an image field and can only handle more complicated image formats such as GIF. Only text-based browsers can handle text as an image.

Because of this drawback, two different methods of generating graphics output will be examined. The first and simpler example converts the number into the X Window System's bitmap format. The second example builds an image through the use of Silicon Graphics' (SGI) Open Inventor graphics libraries (Open Inventor is commercially licensed). By building a scene as an Open Inventor scene database, the scene can be easily converted into SGI's rgb graphics file format. Once the 3D scene is created and stored in the rgb format it will have to be converted to the GIF format through the use of the utilities in the netpbm libraries.

Section 2. The IMG tag

The goal is to count and display the number of times an html document has been loaded by a WWW browser. To execute a program, the IMG tag is used. Before discussing the actual program, the format of the image tag is discussed, and the method for executing a program is given.

I am interested in two flavors of the IMG tag:

<IMG SRC="http://www.this.here.machine/~user/document.name">
<IMG ALIGN=alignment SRC="http://www.this.here.machine/~user/document.name">

The first version simply places an image at the current location and justifies any text to line up with the bottom of the image. The second version allows you to specify where you would like the text to be placed. The alignment option can be either "top" or "middle". By specifying a URL for the image, an executable program can be specified.

The counting program is executed by specifying its URL in the source field of the IMG tag. As long as the output of the program is consistent with the MIME standard, the output will be treated as an image and will be displayed in the appropriate manner. For example, if I have a script called counter in my own cgi directory, ~black/public_html/cgi-bin, and the program can open up the file that is passed as its command line argument, the program can be executed each time a page is loaded into a browser,

<IMG ALIGN=MIDDLE SRC="http://www./~black/cgi-bin/counter?example.dat">

Here I have specified that any text outside of the image be lined up with the middle of the image (the alignment field is optional). When it is called, the program, called counter here, updates the count found in example.dat and displays the current number.

Section 3. Counting Each Time a Page is Viewed

The idea is to execute a program each time a page is accessed. At first glance, the program itself has a simple job to do. It must open a file, increment a counter, update the file, and output the result. The program is to be executed using the IMG tag. The downside is that if the IMG tag is used, the program's output has to be in a graphics format that is understood by the majority of web browsers out there. I will look at two examples, the first will send the output in the X Window System's bitmap format and the second will create a file in SGI's rgb format and then convert it to GIF.

3.1 Simple Test Script

Because the difficulties in creating a counter center on the conversion of output to a convenient graphics format, it is easy to forget that the ultimate goal is to simply count the number of times someone takes a look at your page. The first example is a simple program that will keep track of and print out the number of times it has been run. The two programs whose output is in a graphics format are built upon this example.

To maintain a count, the file in which the count will be stored is passed as a command line argument, so that the same program can be eventually used as a counter for more than one page. The program simply opens the file, if it exists, updates the count, and saves the new count in the file. Given the updated count, an appropriate message is printed. Because a file is opened there are some subtleties involved. The file might not exist, and if not, the count is initialized to one. Furthermore, on a UNIX system the user ftp owns the process and must be able to read and write to the file in the specified directory. If the file does not exist, the file that is created will be owned by ftp, and the user may not be able to read or write to the file without help from the system adminstrator.

[Text of simpleCounter.c++ goes here (Figure 1)]

The example shown in Figure 1 shows the steps required to implement a simple counter. In all of the examples, the program must specify the format of its output. In this example, since the output is to be an HTML text file the first line must tell the browser what to expect:
printf("Content-type:text/html\n\n");
Once the browser knows the format of the program's output, it can react accordingly.

If this program resides in my cgi-bin directory and is called simpleCounter.cgi, the full path name is ~black/public_html/cgi-bin/simpleCounter.cgi. If I want to keep track of the number of times a page has been viewed using a file called ~black/public_html/cgi-bin/data/example.dat, then the program is called using the following URL:
http://www./~black/cgi-bin/simpleCounter.cgi?data/example.dat
When this program is run as a CGI script it sends out the single sentence
"This page has been accessed 12 times",
and the number will be updated each time you reload the file.

3.2 Image in X-Bitmap format

The bad news is that to be used within the IMG tag the program must send its ouput in a graphics format. The simplest thing to do is to use the X Window System's bitmap format. By specifying the bitmap for each number, the final bitmap can be constructed by arranging the bitmap in the proper order. Of course this is easier said than done, so an example is given.

[Text of bitmapCounter.c++ goes here (Figure 2)]

[Text of bitmapCounter.h goes here (Figure 3)]

The example shown in Figures 2 and 3 updates the counter in the same way as the first example. However, the program tests to see if a text based browser is being used. If so, then the output is simply the number and the program exits. If the browser is graphics based, then it converts the number into the X Window System's bitmap format. The individual digits to be displayed are stored in the array VISITS, and this array is used to subscript into the array NUMBER which contains the bitmaps for each number. For example the entries in NUMBER[3][.] contain the bitmap for the number 3. The array NUMBER is defined in the file bitmapCounter.h (Figure 3) and can be easily replaced by the bitmaps or your choice. I used the X Window System's program ``bitmap'' to create the bitmaps and simply converted the output to an array of strings using a Perl script.

The program can be executed using the IMG tag whenever a page is viewed. For example, if the program is called bitmapCounter.cgi, then the image is created when the browser comes across the IMG tag:
<IMG ALIGN=top SRC="http://www./~black/cgi-bin/bitmapCounter.cgi?data/Example.dat>
The output is shown in Figure 4 and compared with the output of the counter in the next section.

3.3 Open Inventor and a 3D counter

If you are not satisfied with the clunky look of the bitmaped images in the previous example, then this example may be more to your liking. The Open Inventor 3D graphics libraries offer an object-oriented environment to display 3D objects. Through the use of a scene database, objects and their orientations can be defined and are displayed in real time, and once the objects are defined the actual render is handled by the routines available in the Open Inventor libraries. Moreover, the libraries allow for the easy conversion of a scene into an image file in Silicon Graphics' rgb format.

The downside is that the image file must be converted into the GIF format so that a graphics based browser can read and display the image. Using the widely available routines found in the netpbm library this can be accomplished, but there are a couple of games that must be played to pipe the final output to stdout. When a web browser askes that ftp start a process on a remote server, and if that process, in turn, forks a child process, the browser will not get any information that is sent to the stdout stream of the child process. For this reason the conversion routines which are forked from the server process cannot send their final output to their stdout stream, but must instead pipe their output to the server process which then sends the output to its standard output.

For this example a brief overview of the Open Inventor scene database is given. For a better description of the libraries, other sources such as Wernecke and the Open Inventor man pages should be examined. After the overview an example is given in which a counter is used to update the number of hits on a web page and the number is converted to text which is then displayed as a 3D object.


[Figure 4. Output for the two counters. The top counter is the output from the Open Inventor program while the bottom is the output from the bitmap version.]

3.3.1 Open Inventor

Open Inventor is a commercially available object-oriented 3D graphics system produced by Silicon Graphics (SGI). The standard graphics libraries that are part of SGI's operating system, IRIX, are the opengl libraries. The opengl libraries are a set of graphics routines explicitly designed to simplify programming 3D graphics routines. The Open Inventor libraries represent a more convenient interface to opengl. To free the programmer from worrying about the the details of rendering an image, the Open Inventor libraries allow the programmer to construct a tree, called the scene database, which defines objects and their transformations.

Once the objects in a scene are defined, the libraries offer many convenience routines that allow for real time viewing and manipulation of the scene. There are also convenience routines that allow the programmer to explicitly create certain actions such as printing an image or picking an object in a scene. One of the fortunate by-products of the graphics conversion process discussed below is that the final GIF output maintains the same view, pixel by pixel. The Open Inventor libraries include routines that can pick a given 3D object by giving a pixel on the view, and the ISMAP option within the IMG tag can be used to find which pixel a user has chosen in an image. In this way the Open Inventor libraries can be used to build interactive 3D graphics on the Web. Once the objects in the scene database are defined, one of Open Inventors greatest strengths is the ease of manipulating the scene.

Objects are defined as one of the simple primitives recognized by Open Inventor (cubes, spheres, and cylinders), a mesh, or a 2D or 3D text field. In this example, a 3D text field is used to display the count. The objects in the scene database can be manipulated in a variety of ways. An object's position can be transformed through a translation, rotation, scaling, or other manipulation of the coordinate axis. The appearance can also be changed by specifying the color properties of an object. Because the order in which translations and color changes matter, the affects of these changes can also be isolated. The Open Inventor libraries are quite extensive and quite powerful. Since I cannot do justice do the full capabilities of Open Inventor, the focus of this section will remain on the basic capabilities.

To demonstrate the structure of a scene database, I will look at a simple example. Once done, the scene database for a 3D string is examined. This will be the scene to be used for the 3D counter that is shown in Figure 4. Once this scene is constructed the image is saved as in SGI's rgb format and converted to GIF.

Before building the more complicated scene database required for the counter, a simple scene database is examined (see Figure 5). The scene is to consist of a red sphere of radius 2.5 centered at the point (2,1,3) and a blue cube centered at (6,4,2) whose sides are length 3.5, 1.5, and 0.5. Before viewing a scene, a camera and light must be defined which will specify exactly what the rendering will contain and how it is viewed. The scene database is a tree and the actions that are performed to render the scene are found by traversing the tree. To do so, start at the very top of the tree, perform the action specified by the current node, and then starting with the left-most child do the same for each child. At any point in the tree, you first perform the action defined by the node, and then move through its children from left to right.

To build the scene database for this example, the top, or ``root'' node is initialized: the first child is a light, the second child is a camera, and there are two objects which are placed in the tree as shown in Figure 5. Because the two objects are to be placed at two different locations a transformation of coordinates is required. By making the objects, a sphere and a cube, the children of a ``separator'', any changes in color and transformation do not affect any other objects that come after them while moving through the tree.


[Figure 5. Scene database for a sphere and a cube.]

Of course, when it comes to adding something like a 3D text item, it is not quite so easy. To display text the font and the font size must be specified. Moreover, the font only defines how the front of the object is to look, a cross-section must also be specified to make it a 3D object. This is done by first specifying a vector containing the coordinates of the profile (perpendicular to the face of each letter) and another vector specifying the order in which the entries in the coordinate vector are to be evaluated. These manipulations are demonstrated in Figure 7, but for a more complete description see Wernecke[pp. 146-153].

The scene database to be constructed must place a single piece of text in the view of the camera. This is shown in Figure 6. The camera is placed on the positive z-axis pointing at the origin. The text is put in place with a ``text'' node which places the text centered at the origin facing towards the positive z-axis. To accentuate the full 3D effect, the scene database will include a transformation so that the camera will look at the text at an angle. This is done by placing the light and camera, and then rotating the coordinate system around the y-axis. For this example, the material or color of the text is set to red. Once the color is set a ``material binding'' node is placed in the scene database. This is done to define the way in which the color is mapped onto the object, which for this example will make the entire text object red. Next the font is defined. Following the font the profile is defined with the index to the profile as the next child. Finally, the text itself is placed at the current origin.


[Figure 6. Scene database for a text object.]

Once the scene database is defined it is saved to an image file in SGI's rgb format. This is done by setting the camera's viewport to a predefined size and rendering the scene in a local buffer. Through the use of the ``Off Screen Rendering'' action the scene is rendered and saved in an image file. The file in which the image is stored is specified through the use of the UNIX tmpnam command. Before exiting this file is removed using the UNIX remove command. When implementing this example you should first find out how these commands are implemented on your UNIX system. If the program happens to exit before removing the file, you can easily leave many image files in one of your temporary directories.

Once the file name is found, the scene is rendered and saved to a file. In this step the principle disadvantage of the Inventor libraries is demonstrated. To be able to render the scene in a local buffer, the library acts as an X Window System's client and must act through the X manager. If the web server that is running a program is not running the X manager, the program will not be able to render the objects defined in the scene database.

[Figure 7. Text of exampleCounter.c++ goes here.]

3.3.2 Converting to GIF Format

Once the scene has been generated and the image has been saved to a temporary file, that image must be converted to the GIF format. In the example shown in Figure 7, two different methods are given. Both methods utilize the netpbm libraries to fork the required conversion routines from the server process. Because a forked process cannot reliably send output to its stdout and be picked up by a web browser, both methods rely on a server process to pass the information, via pipes, to the necessary processes. The difference is that one requires three pipes while the other requires two.

The method using three pipes retrieves the pnm image from the netpbm routine sgitopnm through a pipe, this new image is sent to the ppmtogif conversion routine. The result from the final conversion is then sent to the server process which is then sent to stdout (see Figure 8). The difference between this method and the method using two pipes is that the latter saves the output of the conversion to GIF in another temporary file which is then read by the server process and sent to stdout. In the last step of the method requiring three pipes, the server process reads the output from a pipe after the final conversion process has been terminated. Since some folks get squeamish over this I put both methods in the code. Personally, I prefer using three pipes over the creation of a second temporary file.

For small images such as the counter in these examples, the method using one temporary file is quickest and most convenient. However, for larger files, the method using two temporary files must be used. The method using three pipes requires that all of the output from the final conversion fit within the pipe's buffer. While this is not a problem for the small image of a number, it is a problem for larger images.


[Figure 8. Diagram of pipes used to convert the rgb graphics format to gif.]

For this discussion the more difficult method, the one using three pipes, is discussed. To convert the image formats, the required programs are started via the fork command. The image data is sent back and forth from the server process through pipes. Since the routines send their output through their stdout, the standard output must be redirected through the proper pipe. The steps required are demonstrated in Figure 7, but for a more complete discussion see Curry[pp. 100-109].

To convert the rgb file to the GIF format, the file is read by the sgitopnm program, and its data is piped to the ppmtogif program that is also found in the netpbm distribution. To execute these routines, the server routine forks off two child processes through the use of the fork command. The fork command simply creates a new process which is nearly an exact copy of the original process. The only difference between this new process and the one that spawned it is that the process ID returned by the fork command is zero.

Once the server has forked the two conversion processes, the necessary programs are started with the use of the execl command. Both conversion routines print the converted image to their own standard output. To send this information through a pipe back to the server process each process must first redirect their standard output, stdout. This is done with the use of the dup command which is used to duplicate a file descriptor.

Every open file has a cooresponding file descriptor associated with it. A file descriptor is simply an integer. There are three standard files that are opened by default for any C program, 0 for standard output, 1 for standard input, and 2 for standard error. The dup command accepts a file descriptor and creates a new file descriptor which points to the same file or pipe. The new descriptor takes on the smallest available value. In this way the standard output or input can be redirected by closing it and then duplicating an existing pipe with the coresponding file descriptor.

For example, suppose I want to redirect the standard output of a program to the output given by another file descriptor. In particular, suppose I have an array of integers,
int pipesgi2pnm[2];
and the first entry in the array, pipesgi2ppm[0], contains a file descriptor. To direct the output to the standard output, close the standard output,
close(0);
and then duplicate the file descriptor,
dup(pipesgi2pnm[0]);
After duplicating the file descriptor, any output that would have normally gone to the standard output will now be sent to the file that is associated with the specified file descriptor.

The idea is take advantage of UNIX pipes and send the output of the conversion routines to the server process. Before the conversion processes are forked, file descriptors for new pipes are found using the pipe command. The new file descriptors for a pipe are defined and then the fork command is called. Because the file descriptors are defined before the fork, both the server and the forked process retain the file descriptors. Since a pipe is defined in terms of file descriptors, information can be passed between the two processes in the same way that information is passed between a program and an open file.

The argument for the pipe command is an array of two integers. From the previous example, the file descriptors for a pipe are defined in the array pipesgi2pnm. Before forking a process to convert the image, the pipe command first defines the pipes to send the information between the server and conversion process,

pipe(pipesgi2pnm);

The pipes in the example conform to the convention given in Curry[p. 107]. The pipe from the first file descriptor, pipesgi2pnm[0], is used to send information from the child process to the server process while the pipe from the second file descriptor, pipesgi2pnm[1], is used to send information from the server to the child process.

Once the pipes are defined, the conversion process is forked from the server process. Before the actual conversion program is started the standard output and input are redirected to send and receive information through the pipe. Once done, the conversion program is executed through the use of the execl command. A disadvantage of the execl is that the path to the program must be specifically defined. The way that I have implemented this is by creating a symbolic link from the cgi-bin directory to the specific conversion routine. In this way, if another conversion routine is to be used other programs can be easily substituted with a minimal amount of effort.


Bibliography

Barkakati, N., The Waite Group's Essential Guide to ANSI C, Howard W. Sams & Comp., Indianapolis, IN. 1988.

Currie, David A., Using C on the UNIX System, O'Reilly & Associates, Inc., Sebastopol, CA, USA. 1985.

Gilly, D., Unix in a Nustshell, O'Reilly & Associates, Inc., Sebastopol, CA, USA. 1986.

netpbm man pages. Source files found at ftp.cs.ubc.ca under the ftp/archive/netpbm subdirectory.

Silicon Graphics IRIX 5.3 man pages.

Wall, L. and R. L. Schwartz, Programming Perl, O'Reilly & Associates, Inc., Sebastopol, CA, USA. 1991.

Wernecke, Josie, The Inventor Mentor, Addison-Wesley Publishing Company, Reading, Ma, USA. 1993.