Documentation for GD.pm: Stein Laboratory.

A. Creating a web counter for your home page.

1. Create a file on disk that will store the number of accesses.

This number will be updated everytime the page is requested.

Create

/u/yourUsername/httpd/counters                      (the directory) 
/u/yourUsername/httpd/counters/homepage.counter     (the actual counter)
/u/yourUsername/httpd/counters/homepage.semaphore   (semaphore, for access)
Here's that sequence of actions for me:
tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/httpd
tucotuco.cs.indiana.edu% ls -l counters
counters: No such file or directory
tucotuco.cs.indiana.edu% mkdir counters
tucotuco.cs.indiana.edu% cd counters
tucotuco.cs.indiana.edu% vi homepage.counter
tucotuco.cs.indiana.edu% cat homepage.counter
0
tucotuco.cs.indiana.edu% vi homepage.semaphore
tucotuco.cs.indiana.edu% cat homepage.semaphore
tucotuco.cs.indiana.edu% ls -l homepage*
-rw-r--r--   1 dgerman  students       2 Oct 21 13:09 homepage.counter
-rw-r--r--   1 dgerman  students       0 Oct 21 13:09 homepage.semaphore
tucotuco.cs.indiana.edu% 
2. Create a script that updates the counter and displays the updated value.

Store this script in

httpd/cgi-bin/homepage
Here's the code, with comments:
#!/usr/bin/perl

use GD;                                                    # enough said
print "Content-type: image/gif\n\n";                       # MIME type of produced document
$location = "/u/yourUsername/httpd/counters";              # this is where we keep the counters, one per page
                                                           # there are two files per counter, one semaphore and one datafile 
open (U, "$location/homepage.semaphore");                  # open the semaphore
flock(U, 2);                                               # 2 is for exclusive lock set on the file (resource)
open (M, "$location/homepage.counter");                    # now open the datafile
$x = <M>;                                                  # read the number inside 
close(M);                                                  # close the datafile 
$x += 1;                                                   # increase the number by one
                                                           # at this point the semaphore is locked, if any other hits are
                                                           # occurring now the processes wait for us to release the lock 
open (M, ">$location/homepage.counter");                   # getting ready to update the datafile
print M $x, "\n";                                          # overwrite previous number with the new value 
close(M);                                                  # close the datafile 

flock(U, 8);                                               # finally, release the lock on the semaphore so that someone
                                                           # else that wants to work with the counter can seize it 

                                                           # from here on we're doing display 

$counter = sprintf(" %s ", $x);                            # create a string ($counter) from $x 
$counter_length = length($counter);                        # find out how long it is (how many chars) 
$font_length = 8;                                          # set the font size (length) in pixels 
$font_height = 16;                                         # set the font size (width) in pixels 
$x = $font_length * $counter_length;                       # deduce the length of string to be displayed 
                                                           # this will be the width of the image 
$y = $font_height;                                         # height of image in pixels 

$image = new GD::Image($x, $y);                            # create an image of that width and height 

                                                           # allocate some colors 

$white = $image->colorAllocate(255, 255, 255);             # white 
$blue = $image->colorAllocate(0, 0, 255);                  # blue 
$image->transparent($white);                               # make the gif transparent 
$image->string(gdLargeFont, 0, 0, $counter, $blue);        # print the counter 

print $image->gif;                                         # print the image to STDOUT
Please make sure to use your real username in $location.

3. Finally, insert a call to this script in the page.

tucotuco.cs.indiana.edu% vi index.html
tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/httpd/htdocs
tucotuco.cs.indiana.edu% cat index.html
<html>
<head>
<title>
Home Page
</title>
</head>
<body bgcolor=white>
<h1>Welcome!</h1> 
This page has been accessed <img src="/cgi-bin/homepage"> times. 
</body>
</html>
tucotuco.cs.indiana.edu% 
4. Try it.
In my case: http://tucotuco.cs.indiana.edu:19800

(Use your own server and port number).

5. (Minor) Caveats.

The counter is increased every time the script is accessed.

The script can be called outside the page so the value of the counter is only approximately correct and the counter is an indirect way of counting accesses to the page.

A better solution is to use frames and have one of the frames' source be the output of a script. The script can output the value of the counter as part of the virtual document, because it has complete control over it.

Graphical counters such as the one discussed in this lab are only exercises in using libraries such as GD.pm and they provide the functionality (web counter) when the document delivered is in a static file, and the delivery is done by the server, so no script is involved.


B. Graphical Clock.

Put this script in httpd/cgi-bin/clock amd make it executable. Study the code and add numbers to the clock dial.

      #!/usr/bin/perl

      use GD; print "Content-type: image/gif\n\n"; 
      ($seconds, $minutes, $hour) = localtime(time); 

      $x = $y = 100; $R = $x / 2; $twopi = 2 * 3.141592; 

      $dhx = $R * sin($hour / 12 * $twopi);  # [1]
      $dhy = $R * cos($hour / 12 * $twopi);  # [2]

      $dmx = $R * sin($minutes / 60 * $twopi); 
      $dmy = $R * cos($minutes / 60 * $twopi); 

      $dhy = - $dhy;               # mirror 
      $dhy *= 0.66; $dhx *= 0.66;  # scale
      $dhx += $R;   $dhy += $R;    # translate 

      $ox = $R; $oy = $R;          # translate origin

      $dmy = - $dmy;               # mirror
      $dmx += $R; $dmy += $R;      # translate 

      $image  = new GD::Image($x, $y); 
      $black  = $image->colorAllocate(0, 0, 0); 
      $yellow = $image->colorAllocate(255, 255, 0); 

      $image->line($ox, $oy, int($dmx), int($dmy), $yellow); 
      $image->line($ox, $oy, int($dhx), int($dhy), $yellow); 

      $image->arc($R, $R, 2 * $R, 2 * $R, 0, 360, $yellow); 

      print $image->gif;
      exit(0);
C. Homework5 Hints

A question was raised in class yesterday about the way we were ending our pattern. Specifically our pattern was not general enough because it was not catching URLs that had in their descriptive names HTML tags, such as <font></font> <br> <h1></h1> and the like. I include a modification of the program below for two reasons:

  1. it shows how one can develop a pattern in stages. Start with simple regular expressions (for which you have drawn the finite automaton) and move up to more complicated ones. In the program below remove only one of the comment signs and run it. Then put it back and remove the comment sign below and run it again. This way you're sure you're advancing, rather than wondering with infinite frustration why the program does not print anything.
  2. it solves the problem posed yesterday. It does this by specifically looking for a </a> to end the <a href...> tag. Notice that it uses the non-greedy ? inside the pattern to make sure that we don't extend too far. Hope you find this example useful. Note: please don't turn this example for credit, because it won't be accepted. Implement the method described yesterday in class. .* means any character whatsoever, 0 or more times, ? says go the shortest distance (to the first </a>, which comes after it) and is means that the pattern is case insensitive and s means that the pattern could extend on more than one line (so . could be \n, which by default is not the case). Let me know if you have questions.
    #!/usr/bin/perl
    
    open (AB, "/u/dgerman/httpd/htdocs/benchmark.html");
    @x = <AB>;
    $x = join('', @x); 
    while # ($x =~ s/(<a\s+href)//i) {  
          # ($x =~ s/(<a\s+href\s*=)//i) { 
          # ($x =~ s/(<a\s+href\s*=\s*"[^"]+")//i) { 
          # ($x =~ s/(<a\s+href\s*=\s*"[^"]+"\s*>)//i) {
          # ($x =~ s/(<a\s+href\s*=\s*"[^"]+"\s*>.*?<\/a>)//is) { 
          # ($x =~ s/<a\s+href\s*=\s*"([^"]+)"\s*>.*?<\/a>//is) { 
      print $i++, ". ", $1, "\n"; 
    } 
    close(AB);