The Web page:

You can try these examples using my pre installed CGI script at dougrice.plus.com as used in the examples below.

Cut and paste the code below ( downto the perl script ) into a file called test.htm and save it to your desktop. Open it and it will include in the data from my guest book:
/gb/gbookFXXX.js

Now edit the code in function gbF() to use the data as you wish!

<html>
 <body>
 <script>
 
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
 
   self.document.write( 
     "<Tr>"+
     "<TD >"+ name+ "</TD><TD >"+ email+"</TD><TD >"+ postedOn+"</TD>"+
     "<TD >"+ IPaddress+ "</TD>"+
     "<TD >"+ userfield1+"</TD><TD >"+ userfield2+	"</TD>"+
     "<TD >"+ userfield3+"</TD><TD >"+ userfield4+	"</TD>"+
     "<TD >"+ comments+	"</TD>"+
     "</TR>"
   )
 }
 
 </script>
 
 <h4> One line of the table </h4>
 
 <table border=2 >
 
 <script language="JavaScript" > 
 
  gbF( 
    "name","email","postedOn", "127.0.0.1",
    "userfield1", "userfield2", "userfield3", "userfield4",
    "comments"
  );
 
 </script>
 </table>
 
 <h4> Now include the data from the guestbook </h4>
 
 <table border=2 >
<!-- local guestbook in same place as guestbook.asp --> 
  <script  language = "JavaScript" type = "text/javascript" 
  src = "./gbookFXXX.js" > 
 
  </script>
 
 
<!-- remote guestbook-->
  <script  language = "JavaScript" type = "text/javascript" 
  src = "/gb/gbookFXXX.js" > 
  </script>
 
 
</table> </body> </html>


The Web page - Last Entry First

<html>
 <head>
 <link rel="stylesheet" href="../club.css">
 </head>
 <body>
 <script>
 var opStr = ""
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
   //self.document.write( name+" , "+ postedOn +","+ IPaddress +" ; " )
   thisEntry =
     "<Tr>"+
     "<TD >"+ name+ "</TD><TD >"+ email+"</TD><TD >"+ postedOn+"</TD>"+
     "<TD >"+ IPaddress+ "</TD>"+
     "</TR><TR>"+
     "<TD >"+ userfield1+"</TD><TD >"+ userfield2+	"</TD>"+
     "<TD >"+ userfield3+"</TD><TD >"+ userfield4+	"</TD>"+
     "</TR><TR>"+
     "<TD colspan=5 class=code ><pre>"+ comments+	"</pre></TD>"+
     "</TR>"
   
 
   //self.document.write( thisEntry )
   // if you want last entry first, prepend onto a string for display later.
   opStr = thisEntry  + opStr
 
 }
 
 </script>
 
 <h4> One line of the table </h4>
 
 <table border=2 >
 
 <script language="JavaScript" > 
 
  gbF( 
    "name","email","postedOn", "127.0.0.1",
    "userfield1", "userfield2", "userfield3", "userfield4",
    "comments"
  );
 
 </script>
 </table>
 
 <h4> Now include the data from the guestbook, and display last entry first </h4>
 
 
 <!-- local guestbook in same place as guestbook.asp --> 
 <script> loading	= "/gb/gbookFXXX.js" </script>
 <script  language = "JavaScript"  src = "/gb/gbookFXXX.js"  type = "text/javascript">  </script>
 
 <!-- Now Dump the Table -->
 <table border=2 >
 <script>
   self.document.write( opStr )
 </script>
 </table>
 
 </body>
 </html>
 

The Original Perl Script: guestbook.pl was adapted from Matt Wright's Script

BT Internet had a guestbook. If I could wrap the data uploaded from the form as:

 
 gbA[ count ] = new GB( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments)
 
 later:-
 
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments );
 
and store this in /gb/gbookFXXX.js I could use this on my web pages.
 Include remote guestbook and gbF() will be called once per entry.
   <script  language = "JavaScript" type = "text/javascript" 
     src = "/gb/gbookFXXX.js" >  
   </script>
 

The sailing club used BT Internet at first. I had an idea and modified a script I found online at: https://www.scriptarchive.com/guestbook.html

function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments );

I tried this out using Xitami on my Windows PC. A copy of the cgi script is at: early version

I recently found a very early versions: gbook_20010403.pl based on https://www.scriptarchive.com/guestbook.html

This worked and was later modified over the years. This required learning enough PERL.

At the time the server was accessed using a dial up modem over the landline.

Early notes and example: /www/hp/gbbook/funcTable.htm

The Perl Script: guestbook.pl

Cut and paste the code below into a file called guestbook.pl.

FTP this file to your web server's CGI area as TEXT, and set file permissions to "chmod 755" or execute (-rwxr-xr-x).

You will need to configure the examples to point to this script.

You will need to configure the script below to a directory that wish to save you guestbook in and can write to.

Now modify the examples to include your guestbook.

This script saves to a file gbookF<guestbook>.js where guestbook is passed up with the form.

The perl script at dougrice.plus.net saves to:
/gb/gbookF<guestbook>.js

Where <guestbook> is the value in the form field: guestbook

A version from 2003: guestbook_20030224.pl

 #!/usr/bin/perl
 #
 ##############################################################################
 #Guestbook that appends form Data as JavaScript Functions
 #
 # control form fields:
 # 	guestbook 	used in file name
 #       loadnextpage 	if defined use URL in nextpage to replace this page.
 #       nextpage	URL to replace 
 #
 #Doug Rice, Copyright 2002,2003
 ##############################################################################
 #
 # use the code below to pipe  script errors to the browser
 use CGI::Carp ( fatalsToBrowser);
 
 # =============================================
 # SEC 2.0 - get FORM fields 
 # =============================================
 # if Method=GET , the browser sends the fields in QUERY_STRING
 # if Method=POST , the browser sends the fields in STDIN
 #
 # HTML forms provide three methods of encoding.
 # application/x-www-form-urlencoded (the default)
 # multipart/form-data
 # text/plain
 #
 # enctype="multipart/form-data" is not suported
 #
 # If method=POST read STDIN
 read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
 
 # If method=GET  the field valuses are decode QUERY_STRING
 #@pairs = split(/&/, $ENV{'QUERY_STRING'}."&".$buffer );
 
 # Use Socket to reverse DNS IP address 
 use Socket;
 
 #
 # Try and reverse lookup IP address.
 #
 ($name, $aliases, $addrtype, $length, @addrs) = gethostbyaddr( inet_aton( $ENV{'REMOTE_ADDR'} ), PF_INET );
 
 # specify  the path to the guestbook file  gbookFXXXX.js
 # XXXX is value of hidden form field:  guestbook 
 #
 # Ensure that gbookFXXXX.js has write permissions
 #
 # if the directory has execute permissions, this script will create gbookFXXXX.js
 
 
 # You cannot include the file from the cgi-bin directory: 
 # /usr/lib/cgi-bin
 
 # Save the file in:
 # /var/www/html/gb
 # Webpage would include the guestbook from the cgi webserver address
 #<script  language = "JavaScript" type = "text/javascript" 
 #   src = "http://127.0.0.1/gb/gbookFXXX.js" >  
 #</script >
 
 
 # so save in an adjacent branch 
 # or the relative path to a directory called gb
 $guestbookjsf  = './../gb/gbookF';
 
 #
 # Find out where the documents are stored. 
 $doc_root = $ENV{'DOCUMENT_ROOT'}.$ENV{'C_DOCUMENT_ROOT'};
 # $guestbookjsf  = $doc_root.'/gb/gbookF';
 
 # =============================================
 # SEC 2.0 - process FORM fields 
 # =============================================
 #
 # If method=POST read STDIN
 # 
 # If method=GET  decode QUERY_STRING assuming 
 # 
 #HTML forms provide three methods of encoding.
 # application/x-www-form-urlencoded (the default)
 # multipart/form-data
 # text/plain
 #
 # enctype="multipart/form-data" is not suported
 
 # Get the input, i.e the form fields pushed up to the CGI script.
 # If method=POST read STDIN
 
 #Combine the query string and fields from STDIN
 
 
 # Split the name-value pairs
 
 # If method=GET  the field valuses are decode QUERY_STRING
 @pairs = split(/&/, $ENV{'QUERY_STRING'}."&".$buffer );
 
 #
 #
 #
 foreach $pair (@pairs) {
 
    ($name, $value) = split(/=/, $pair);
 
    # Un-Webify plus signs and %-encoding
    $value =~ tr/+/ /;
    # Leave the value mostly escaped as JavaScript can unescape easily.
 
    # Convert CR LF and LF to <BR>\ at the end of the line
    $value =~ s/%0D//g;
    $value =~ s/%0A/<BR>\\\n/g;
 
 
    # Convert \ " and ''
    $value =~ s/%5C/\\\\/g;
    $value =~ s/%22/\\"/g;
    $value =~ s/%27/\\'/g;
    
    # Convert HTML < &lt; and > &gt; so they display, 
    # 
    $value =~ s/%3C/&lt;/g;
    $value =~ s/%3E/&gt;/g;
    
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    $FORMJS{$name} = $value;
 }
 
 # =============================================
 # SEC 2.0 - try some anti spam  
 # =============================================
 
   #
   # Use email to validate user and filter spam 
   #
   $unlock = 'lock'; 
 
   if ( $FORMJS{'email'} eq 'dem0@dem0' ){
     $unlock = 'unlock';
     # blank off email to hide what worked
     $FORMJS{'email'}     = '';
 
     # force to one guest book
     # $FORMJS{'guestbook'} = 'XXX';
   }
 
   #if ( $FORMJS{'email'} eq 'env' ){
   #  print "\n" . '========== @ENV - foreach =========='."\n";
   #  foreach $key (sort(keys %ENV)) {
   #    print "\n$key," . '=' .  $ENV{ $key } ;
   #  }  
   #}
 
 
   if ( $unlock eq 'unlock' ){
 
 	# If unlock make guestbook files RW
     #
     # print `chmod 660 ../public/gb/gbook*.js` ; 
     # print `chmod 660 /var/www/html/gb/gbook*.js` ;
     # run chmod on server to make files read write   
     # print `$unlockcmd $guestbookjsf$FORMJS{'guestbook'}.js` ;     
   }
 
   $date ="26 oct 2002 09:00;"; # date formatted so that JavaScript can parse it
   &setDate();
 
 
   # =============================================
   # SEC 2.0 - Build up string  
   # =============================================
 
   # take form fields and wrap them in the JavaScript Function call and append it onto the end of "gbookF$FORMJS{'guestbook'}.js"
 
   # Take the form fields and output them wrapped in the format:
 
   #gbF( 
   #"name","email"," sun 23/Jun/2002 at 20: 52: 0 ","127.0.0.1",
   #"userfield1",
   #"userfield2",
   #"userfield3",
   #"userfield4",
   #"comments"
   #);
 
   $N = $FORMJS{'guestbook'};
 
   # get time stamp 
   &setDate();
 
   #
   # Build up string:-
   #
 
   # record which page and what browser was used.
   $gbStr="";
   $gbStr .= "// Added by gbook.pl  $ENV{'HTTP_REFERER'} \n";
   $gbStr .= "// HTTP_USER_AGENT:  $ENV{'HTTP_USER_AGENT'} \n";
 
 
   $gbStr .= "gbF( \n";
   $gbStr .= "\"$FORMJS{'name'}\",";
   $gbStr .= "\"$FORMJS{'email'}\",";
   $gbStr .= "\"$date\",";
   $gbStr .= "\"$ENV{'REMOTE_ADDR'}\",\n";
   $gbStr .= "\"$FORMJS{'userfield1'}\",\n";
   $gbStr .= "\"$FORMJS{'userfield2'}\",\n";
   $gbStr .= "\"$FORMJS{'userfield3'}\",\n";
   $gbStr .= "\"$FORMJS{'userfield4'}\",\n";
   $gbStr .= "\"$FORMJS{'comments'}\"\n";
   $gbStr .= ");\n";
   
   # alternative using . instead of .= ;
     $gbStr="" .
          "// Added by gbook.pl  $ENV{'HTTP_REFERER'} \n" .
          "// HTTP_USER_AGENT:  $ENV{'HTTP_USER_AGENT'} \n" .
 
 
          "gbF( \n" .
          "\"$FORMJS{'name'}\"," .
          "\"$FORMJS{'email'}\"," .
          "\"$date\"," .
          "\"$ENV{'REMOTE_ADDR'}\",\n" .
          "\"$FORMJS{'userfield1'}\",\n" .
          "\"$FORMJS{'userfield2'}\",\n" .
          "\"$FORMJS{'userfield3'}\",\n" .
          "\"$FORMJS{'userfield4'}\",\n" .
          "\"$FORMJS{'comments'}\"\n" .
          ");\n";
   
 
   
   
   
   #Lock file while updating file to prevent race conddition update.
   # This has not been a big problem as the script does not take long.
   # A simple edit was needed to remove corrupted posts.
   
   # This blocks if file is locked, and removed when script ends.
 
   flock GUESTFJS, 2 || &file_error( GUESTJS, "lock: $guestbookjsf$N.js" );
   
   $N = $FORMJS{'guestbook'};
   open (GUESTFJS,">>$guestbookjsf$N.js") || &file_error( GUESTJS, "$guestbookjsf$N.js" );   
   print GUESTFJS $gbStr;
   close (GUESTFJS);
 
   if ( $FORMJS{'loadnextpage'} ){
 
     # Print Out Initial Output Location Heading
     print "Location: $FORMJS{'nextpage'}\n\n";
     exit;
   }
 
 
   # =============================================
   # SEC 2.0 - Exit  inline code
   # =============================================
 
   #
   # Exit script and reload page submitting form
   # 
   # remove ?query string and send a new one which can indicate problems
   # if URL ends 
   # ?ok  - successfull
   # 
   ($fromURL, $fromQuerystring) = split(/\?/,$ENV{'HTTP_REFERER'} );
   $qs = "?ok";
   
   # WARNING: Location: ?ok   Do not allow this. It spun Edge and filled up log file and MAXED out DiskQuota !
 
   #print "Location: $fromURL$qs \n\n";
   #exit;
 
   # else print a page to thank form submission.
   # Print Beginning of HTML
   print "Content-Type: text/html\n\n";
   print "<!DOCTYPE HTML >\n<html><head><title>Thank You</title></head>\n";
   print "<body><h1>Thank You For your submission</h1>\n";
 
   if ( $FORMJS{'nextpage'} ){
     #print "Location: $FORMJS{'nextpage'} \n\n";  
     print "<H1><A HREF=$FORMJS{'nextpage'}"."?reload "." target=_top > $FORMJS{'nextpage'}  : return... <A></H1>";
   }
   
   if ( $ENV{'HTTP_REFERER'} ) {
     print "<H1><A HREF=$ENV{'HTTP_REFERER'}"."?reload "." target=_top > $ENV{'HTTP_REFERER'}  : return... <A></H1>";
   }
 
 
   print "<H1><A HREF=$FORMJS{'nextpage'}"."?reload"." target=_top > return... <A></H1>";
 
   # print out the gbF() added to  the file.
   print "<PRE>";
   print $gbStr;
   print "</PRE>";
 
   # Output pertinent ENV 
   print "<PRE>";
   print $ENV{'HTTP_REFERER'};
   print "\n";
   print $ENV{'REQUEST_METHOD'};
   print "\n";
   print $ENV{'CONTENT_LENGTH'};
   print "\n";
   print $ENV{'CONTENT_TYPE'};
   print "\n";
   print $ENV{'QUERY_STRING'};
   print "\n";
   print $buffer;
   print "\n";
 
   # dump the ENV 
   print "\n" . '========== @ENV - foreach =========='."\n";
   foreach $key (sort(keys %ENV)) {
       print "\n$key" . '=' .  $ENV{ $key } ;
   }  
   print "</PRE>";
 
   # print out the directory and list the directory where the guestbooks are:-
   print "<PRE>";
   print `pwd`;
   print `ls -l ../gb/*`;
   print "</PRE>";
 
 
 
   print '</body></html>';
 
   exit;
 
 
 #######################
 # Subroutines
 
 # file_error - call if problems opening files
 sub file_error {
 
     local($FH, $fileName) = @_; 
 
 
     #
     # Exit script and reload page submitting form
     # 
     # remove ?query string and send a new one which can indicate problems
     # if URL ends 
     # ?ok  - successfull
     # 
     # WARNING 
     # I had an idea to use HTTP_REFERER and append ?ok to return to the calling page and add an exit code.
     #
     # if I called 192.168.1.x/gb.php there is no HTTP_REFERER and I got too many redirects
     #  - Edge does not like Location: ?ok 
     #
 
 
     ($fromURL, $fromQuerystring) = split(/\?/,$ENV{'HTTP_REFERER'} );
     $qs = "?error:file:$fileName ";
   
     print "Location: $fromURL$qs \n\n";
     exit;
 
 
     # Print Beginning of HTML
     print "Content-Type: text/html\n\n";
     print "<html><head><title>Thank You</title></head><body>\n";
     print "<H1>Error Opening File $fileName:, press back</h1> \n";
     print "Your data has not been added, press back <P>\n";
 
     print "If you can log into the server, check file permissions\n";
     print '</body></htmr>';
     exit;
 }
 
 ################################################################
 # setDate setup $date to string that javascript can parse using:  now= new Date( postedOn )
 #
 
 sub setDate {
 
 # Get the Date for Entry
 # output date so that the web pages can be use the javascript:  now= new Date( postedOn )
 
 #IE 5 formats toUTCString():		Tue, 25 Jun 2002 05:47:32 UTC
 #Netscape Navigator toUTCString():	Tue, 25 Jun 2002 05:49:59 GMT
 
 #($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);		   
 #  0     1     2    3     4     5    6     7     8
 @t = localtime();
 @days=  ('Sun','Mon','Tue','Wed','Thu','Fri','Sat','Sun');
 @months=('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
 $fullYear=1900+$t[5];
 #toUTCString():		#Tue, 25 Jun 2002 05:47:32 UTC
 $date = "$days[$t[6]], $t[3] $months[$t[4]] $fullYear ".substr("0".$t[2],-2,2).":".substr("0".$t[1],-2,2).":".substr("0".$t[0],-2,2)." UTC+0100";
 
 
 
 #
 # It might be better to use the ISO format: YYYY-MM-DDTHH:mm:ss.sssZ as it sorts
 #
 #($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);		   
  
 #ISO format:-
 #(YYYY-MM-DDTHH:mm:ss.sssZ or ±YYYYYY-MM-DDTHH:mm:ss.sssZ, respectively). The timezone is always UTC, as denoted by the suffix Z
 
 #$date = sprintf("%04d-%02d-%02dT%02d:%0d:%02d.000Z",$fullYear,($mon+1),$mday,($hour-$isdst),$min,$sec );
 
 }
 

buttons to submit using the different options

these buttons submit the FORM.

 <input type="submit" name="B1" value="Submit-get"        formmethod=get >
 <input type="submit" name="B1" value="Submit-post url"   formmethod=post formenctype="application/x-www-form-urlencoded">
 <input type="submit" name="B1" value="Submit-post multi" formmethod=post formenctype="multipart/form-data" >
 <input type="submit" name="B1" value="Submit-post plain" formmethod=post formenctype="text/plain" >
 

they overide the method and enctype used below:-

 <form name="F1" 
   action ="value_set_up_by_javaScript" 
   method ="GET" 
 
   method ="POST" 
   
   enctype="application/x-www-form-urlencoded"
   enctype="multipart/form-data"
   enctype="text/plain"
 
 >
 

Another version that needs a tidy

 #!/usr/bin/perl
 #!perl
 #
 ##############################################################################
 #Guestbook that appends form Data as JavaScript Functions
 #
 # control form fields:
 # 	guestbook 	used in file name
 #       loadnextpage 	if defined use URL in nextpage to replace this page.
 #       nextpage	URL to replace 
 #
 # To install this code. Copy this into a file called guestbook.pl
 # FTP up to your Web server from your PC as a TEXT file.
 # NOTE: I have found that AUTO sometimes does not work, 
 # as the .pl extenstion causes it to be thought of as BINARY 
 # and some CGI perl imterpreters do not like CRLF
 # 
 # Now ensure that the file permissions are set to execute
 # NOTE: Don't forget to set the file permissions to execute (-rwxr-xr-x) 
 # NOTE: Upload as TEXT or ASCII. Auto sometimes does not work if perl is installed on the PC.
 # NOTE: If you get Internal server error, check that your path to perl above is correct.
 # NOTE: If you get Internal server error, check that your FTP up script as TEXT
 # NOTE: If you get Internal Server error, check if your file paths are correct.
 # Doug Rice, Copyright 2002,2003,..,2025
 ##############################################################################
 #
 # Protect script from Spin and Limit to using FORM type= post
 # It is possible to spin this if you use  href="?ok" >return
 # or Location: ?ok
 #
 # /cgi-bin/guestbook.pl?ok
 # and Location: ?ok 
 # caused a spin which was difficult to stop.
 #
 # check that the form used POST.
 # CONTENT_TYPE=application/x-www-form-urlencoded
 # If method=POST read STDIN
 # 
 # If method=GET  decode QUERY_STRING assuming 
 #
 # example "application/x-www-form-urlencoded" fields
 #
 # guestBookArrayName=&name=name&email=email&password=&
 # userfield1=userfield1&userfield2=userfield2&userfield3=userfield3&userfield4=userfield4&
 # comments=&
 # nextpage=;B1=Submit
 #
 #HTML forms provide three methods of encoding.
 # application/x-www-form-urlencoded (the default)
 # multipart/form-data
 # text/plain
 #
 # only "application/x-www-form-urlencoded" is supported
 #
 
 
 # If you are getting Internal server error messages,
 # use the code below to pipe  script errors to the browser
 use CGI::Carp ( fatalsToBrowser);
 
 
 # Get the input, i.e the form fields pushed up to the CGI script.
 # REQUEST_METHOD,=GET
 
 # If method=POST read STDIN
 read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
 
 # If method=GET  the field valuses are decode QUERY_STRING
 #@pairs = split(/&/, $ENV{'QUERY_STRING'}."&".$buffer );
 
 
 
 # Ensure method="POST" has been used, 
 if ( $ENV{'CONTENT_TYPE'} ){
 
 # check that the form used POST.
 # assume it is 
 # CONTENT_TYPE=application/x-www-form-urlencoded
 
 # Get the input, i.e the form fields pushed up to the CGI script.
 
 # If method=POST read STDIN
 #read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
 #
 } else {
 
   # else print a page to decline form submission.
   #
   # Print Beginning of HTML
   print "Content-Type: text/html\n\n"  ;
   print '<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=us-ascii"><title>Thank You - test</title></head>' ;
   print "<body><h1>Thank You For your interest. It has not been possible to proceed, this time.</h1>\n" ;
 
 #  print "</body></html>";
 #  exit;
 
 
   print "<H3>ENV{'HTTP_REFERER': $ENV{'HTTP_REFERER'} </H3>";
   print "<H3>ENV{'CONTENT_TYPE'}:$ENV{'CONTENT_TYPE'} </H3>";
   
   print "\n" . '========== @ENV - foreach =========='."\n<pre>";
   foreach $key (sort(keys %ENV)) {
     print "\n$key," . '=' .  $ENV{ $key } ;
   }
 
   print "\n</pre><h1>STDIN: used when method=post </h1><pre>\n";
 
   # Get the input, i.e the form fields pushed up to the CGI script.
   # If method=POST read STDIN
   #read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
   #print $buffer;
 
   print "</pre></body></html>";
 
   exit;
 }
 
 
 
 
 # Alternatively code like:
 # uncomment line below to view compiler errors 
 #
 #BEGIN { 
 #$| = 1; 
 #open (STDERR, ">&STDOUT"); 
 #print "Content-type: text/html\n\n<pre>\n"; 
 #} 
 
 
 # Use Socket to reverse DNS IP address 
 use Socket;
 
 #
 # Try and reverse lookup IP address.
 #
 ($name, $aliases, $addrtype, $length, @addrs) = gethostbyaddr( inet_aton( $ENV{'REMOTE_ADDR'} ), PF_INET );
 
 
 
 
 # specify  the path to the guestbook file  gbookFXXXX.js
 # XXXX is value of hidden form field:  guestbook 
 #
 # Ensure that gbookFXXXX.js has write permissions
 #
 # if the directory has execute permissions, this script will create gbookFXXXX.js
 
 
 # You cannot include the file from the cgi-bin directory: 
 # /usr/lib/cgi-bin
 
 # Save the file in:
 # /var/www/html/gb
 # Webpage would include the guestbook from the cgi webserver address
 #<script  language = "JavaScript" type = "text/javascript" 
 #   src = "http://127.0.0.1/gb/gbookFXXX.js" >  
 #</script >
 
 
 # so save in an adjacent branch 
 # or the relative path to a directory called gb
 $guestbookjsf  = './../gb/gbookF';
 
 #
 # Find out where the documents are stored. 
 # One should work.
 #
 # On  CGI servers I can use: $ENV{'C_DOCUMENT_ROOT'}
 # On plus.net CGI server I had to use: $ENV{'C_DOCUMENT_ROOT'}
 #
 $doc_root = $ENV{'DOCUMENT_ROOT'}.$ENV{'C_DOCUMENT_ROOT'};
 
 # $guestbookjsf  = $doc_root.'/gb/gbookF';
 
 #
 #
 # =============================================
 # SEC 2.0 - process FORM fields sent using POST
 # =============================================
 #
 # Get the input, i.e the form fields pushed up to the CGI script. 
 #
 # This assumes the form uses the default "application/x-www-form-urlencoded "
 # CONTENT_TYPE=application/x-www-form-urlencoded
 #
 # example:
 #  appendJS=&noappendHTML=&guestBookArrayName=&
 #  name=name&email=email&password=&
 #  userfield1=userfield1&userfield2=userfield2&userfield3=userfield3&userfield4=userfield4&
 #  comments=&
 #  nextpage=;B1=Submit
 #
 #  method ="POST" 
 #  method ="GET" 
 #
 # If method=POST read STDIN
 # 
 # If method=GET  decode QUERY_STRING assuming 
 #
 #HTML forms provide three methods of encoding.
 # application/x-www-form-urlencoded (the default)
 # multipart/form-data
 # text/plain
 #
 # enctype="multipart/form-data" is not suported
 
 # Get the input, i.e the form fields pushed up to the CGI script.
 # If method=POST read STDIN
 
 # If 
 
 # read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
 
 
 
 #Combine the query string and form fields
 # Split the name-value pairs
 
 # If method=GET  the field valuses are decode QUERY_STRING
 @pairs = split(/&/, $ENV{'QUERY_STRING'}."&".$buffer );
 
  
 
 #
 #
 #
 foreach $pair (@pairs) {
 
    ($name, $value) = split(/=/, $pair);
 
    # Un-Webify plus signs and %-encoding
    $value =~ tr/+/ /;
    # Leave the value mostly escaped as JavaScript can unescape easily.
 
    # Convert CR LF and LF to <BR>\ at the end of the line
    $value =~ s/%0D//g;
    $value =~ s/%0A/<BR>\\\n/g;
 
 
    # Convert \ " and ''
    $value =~ s/%5C/\\\\/g;
    $value =~ s/%22/\\"/g;
    $value =~ s/%27/\\'/g;
 
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    $FORMJS{$name} = $value;
 }
 
 
   #
   # Use email to validate user and filter spam 
   #
   $unlock = 'lock'; 
 
   if ( $FORMJS{'email'} eq 'dem0@dem0' ){
     $unlock = 'unlock';
     # blank off email to hide what worked
     $FORMJS{'email'}     = '';
 
     # force to one guest book
     # $FORMJS{'guestbook'} = 'XXX';
   }
 
   #if ( $FORMJS{'email'} eq 'env' ){
   #  print "\n" . '========== @ENV - foreach =========='."\n";
   #  foreach $key (sort(keys %ENV)) {
   #    print "\n$key," . '=' .  $ENV{ $key } ;
   #  }  
   #}
 
 
   if ( $unlock eq 'unlock' ){
 
 	# If unlock make guestbook files RW
     #
     # print `chmod 660 ../public/gb/gbook*.js` ; 
     # print `chmod 660 /var/www/html/gb/gbook*.js` ;
     # run chmod on server to make files read write   
     # print `$unlockcmd $guestbookjsf$FORMJS{'guestbook'}.js` ;     
   }
 
 
 
 
 $date ="26 oct 2002 09:00;"; # date formatted so that JavaScript can parse it
 &setDate();
 
   # take form fields and wrap them in the JavaScript Function call and append it onto the end of "gbookF$FORMJS{'guestbook'}.js"
 
   # Take the form fields and output them wrapped in the format:
 
   #gbF( 
   #"name","email"," sun 23/Jun/2002 at 20: 52: 0 ","127.0.0.1",
   #"userfield1",
   #"userfield2",
   #"userfield3",
   #"userfield4",
   #"comments"
   #);
 
   $N = $FORMJS{'guestbook'};
 
   open (GUESTFJS,">>$guestbookjsf$N.js") || &file_error( GUESTJS, "$guestbookjsf$N.js" ); 
 
   #Lock file while updating file to prevent race conddition update.
   # This has not been a big problem as the script does not take long.
   # A simple edit was needed to remove corrupted posts.
   
   # This blocks if file is locked, and removed when script ends.
 
   flock GUESTFJS, 2 || &file_error( GUESTJS, "lock: $guestbookjsf$N.js" );
   # get time stamp 
   &setDate();
 
   # record which page and what browser was used.
   print GUESTFJS "// Added by gbook.pl  $ENV{'HTTP_REFERER'} \n";
   print GUESTFJS "// HTTP_USER_AGENT:  $ENV{'HTTP_USER_AGENT'} \n";
 
 
   print GUESTFJS "gbF( \n";
   print GUESTFJS "\"$FORMJS{'name'}\",";
   print GUESTFJS "\"$FORMJS{'email'}\",";
   print GUESTFJS "\"$date\",";
   print GUESTFJS "\"$ENV{'REMOTE_ADDR'}\",\n";
   print GUESTFJS "\"$FORMJS{'userfield1'}\",\n";
   print GUESTFJS "\"$FORMJS{'userfield2'}\",\n";
   print GUESTFJS "\"$FORMJS{'userfield3'}\",\n";
   print GUESTFJS "\"$FORMJS{'userfield4'}\",\n";
   print GUESTFJS "\"$FORMJS{'comments'}\"\n";
   print GUESTFJS ");\n";
   close (GUESTFJS);
 
   #
   # Alternative format:-
   #
 
 
   # record which page and what browser was used.
   $gbStr="";
   $gbStr .= "// Added by gbook.pl  $ENV{'HTTP_REFERER'} \n";
   $gbStr .= "// HTTP_USER_AGENT:  $ENV{'HTTP_USER_AGENT'} \n";
 
 
   $gbStr .= "gbF( \n";
   $gbStr .= "\"$FORMJS{'name'}\",";
   $gbStr .= "\"$FORMJS{'email'}\",";
   $gbStr .= "\"$date\",";
   $gbStr .= "\"$ENV{'REMOTE_ADDR'}\",\n";
   $gbStr .= "\"$FORMJS{'userfield1'}\",\n";
   $gbStr .= "\"$FORMJS{'userfield2'}\",\n";
   $gbStr .= "\"$FORMJS{'userfield3'}\",\n";
   $gbStr .= "\"$FORMJS{'userfield4'}\",\n";
   $gbStr .= "\"$FORMJS{'comments'}\"\n";
   $gbStr .= ");\n";
   
   
   #Lock file while updating file to prevent race conddition update.
   # This has not been a big problem as the script does not take long.
   # A simple edit was needed to remove corrupted posts.
   
   # This blocks if file is locked, and removed when script ends.
 
   flock GUESTFJS, 2 || &file_error( GUESTJS, "lock: $guestbookjsf$N.js" );
   
   $N = $FORMJS{'guestbook'};
   open (GUESTFJS,">>$guestbookjsf$N.js") || &file_error( GUESTJS, "$guestbookjsf$N.js" );   
   print GUESTFJS $gbStr;
   close (GUESTFJS);
 
 
 
 
 
   if ( $FORMJS{'loadnextpage'} ){
 
     # Print Out Initial Output Location Heading
     print &quot;Location: $FORMJS{'nextpage'}\n\n&quot;;
     exit;
   }
 
 
   # =============================================
   # SEC 2.0 - Exit  inline code
   # =============================================
 
   #
   # Exit script and reload page submitting form
   # 
   # remove ?query string and send a new one which can indicate problems
   # if URL ends 
   # ?ok  - successfull
   # 
   ($fromURL, $fromQuerystring) = split(/\?/,$ENV{'HTTP_REFERER'} );
   $qs = "?ok";
   
   # WARNING: Location: ?ok   Do not allow this. It spun Edge and filled up log file and MAXED out DiskQuota !
 
   #print "Location: $fromURL$qs \n\n";
   #exit;
 
 
 
 
 
   # else print a page to thank form submission.
   # Print Beginning of HTML
   print "Content-Type: text/html\n\n";
   print "<html><head><title>Thank You</title></head>\n";
   print "<body><h1>Thank You For your submission</h1>\n";
   print "<H1><A HREF=$FORMJS{'nextpage'}"."?reload"." target=_top > return... <A></H1>";
 
   if ( $FORMJS{'nextpage'} ){
     #print "Location: $FORMJS{'nextpage'} \n\n";  
     print "<H1><A HREF=$FORMJS{'nextpage'}"."?reload "." target=_top > $FORMJS{'nextpage'}  : return... <A></H1>";
   }
   
   if ( $ENV{'HTTP_REFERER'} ) {
     print "<H1><A HREF=$ENV{'HTTP_REFERER'}"."?reload "." target=_top > $ENV{'HTTP_REFERER'}  : return... <A></H1>";
   }
 
 
 
 
 
 
   print '</body></html>';
 
   exit;
 
 
 #######################
 # Subroutines
 
 # file_error - call if problems opening files
 sub file_error {
 
     local($FH, $fileName) = @_; 
 
 
     #
     # Exit script and reload page submitting form
     # 
     # remove ?query string and send a new one which can indicate problems
     # if URL ends 
     # ?ok  - successfull
     # 
     # WARNING 
     # I had an idea to use HTTP_REFERER and append ?ok to return to the calling page and add an exit code.
     #
     # if I called 192.168.1.x/gb.php there is no HTTP_REFERER and I got too many redirects
     #  - Edge does not like Location: ?ok 
     #
 
 
     ($fromURL, $fromQuerystring) = split(/\?/,$ENV{'HTTP_REFERER'} );
     $qs = "?error:file:$fileName ";
   
     print "Location: $fromURL$qs \n\n";
     exit;
 
 
     # Print Beginning of HTML
     print "Content-Type: text/html\n\n";
     print "<html><head><title>Thank You</title></head><body>\n";
     print "<H1>Error Opening File $fileName:, press back</h1> \n";
     print "Your data has not been added, press back <P>\n";
 
     print "If you can log into the server, check file permissions\n";
     print '</body></htmr>';
     exit;
 }
 
 ################################################################
 # setDate setup $date to string that javascript can parse using:  now= new Date( postedOn )
 #
 
 sub setDate {
 
 # Get the Date for Entry
 # output date so that the web pages can be use the javascript:  now= new Date( postedOn )
 
 #IE 5 formats toUTCString():		Tue, 25 Jun 2002 05:47:32 UTC
 #Netscape Navigator toUTCString():	Tue, 25 Jun 2002 05:49:59 GMT
 
 #($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);		   
 #  0     1     2    3     4     5    6     7     8
 @t = localtime();
 @days=  ('Sun','Mon','Tue','Wed','Thu','Fri','Sat','Sun');
 @months=('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
 $fullYear=1900+$t[5];
 #toUTCString():		#Tue, 25 Jun 2002 05:47:32 UTC
 $date = "$days[$t[6]], $t[3] $months[$t[4]] $fullYear ".substr("0".$t[2],-2,2).":".substr("0".$t[1],-2,2).":".substr("0".$t[0],-2,2)." UTC+0100";
 
 
 
 #
 # It might be better to use the ISO format: YYYY-MM-DDTHH:mm:ss.sssZ as it sorts
 #
 #($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);		   
  
 #ISO format:-
 #(YYYY-MM-DDTHH:mm:ss.sssZ or ±YYYYYY-MM-DDTHH:mm:ss.sssZ, respectively). The timezone is always UTC, as denoted by the suffix Z
 
 #$date = sprintf("%04d-%02d-%02dT%02d:%0d:%02d.000Z",$fullYear,($mon+1),$mday,($hour-$isdst),$min,$sec );
 
 }
 
 

example HTTP header that asked user to save data.csv

 HTTP/1.1 200 OK
 Date: Tue, 29 Jul 2025 06:43:28 GMT
 Content-Disposition: attachment; filename=data.csv
 Content-Type: text/csv
 Connection: keep-alive
 

Perl to output header and csv data to be saved by user.

   #!/usr/bin/perl
   # t.pl
   # chmod 755 t.pl  
   # for CSV files
   print "Content-Type: text/csv\n"  ;
   print "Content-Disposition: attachment; filename=test.csv\n"  ;
   # need a blank line
   print "\n";
   print "1,2,3,4"."\n"; 
   print "1,2,3,4"."\n";
   print "1,2,3,4"."\n";
   print "10,2,3,4"."\n";
   exit;
 

example Perl multi-line string

  $body2 =<<'END';
 
 <h1>Thank You For your interest.</h1>
 <P> It has not been possible to proceed, this time.</p>
  
 END
 
 print $body2;
 
 

example HTMP header to download inline gif file. I could run a cgi-bin script to serve a img and capture who was visiting the files on www.dougrice.plus.com

 HTTP/1.1 200 OK
 Date: Tue, 29 Jul 2025 06:43:28 GMT
 Connection: keep-alive
 Content-Disposition: filename=ipstats2.gif
 Content-Type: image/gif
 
 

Perl code to open gif file and output it. If the user right clicks the image, they can save ipstats2.gif instead of the name of the perl script.

 #
 # ensure the following are set up
 # use Content-Disposition to specify file name if saved using save image
 $picFile  = ".//ipstats.gif";
 $header   = "Content-Disposition: filename=ipstats2.gif\nContent-Type: image/gif\n\n";
 open (PICFILE,"$picFile") || die "Can't Open $picFile: $!\n";
 binmode PICFILE;
 syswrite( STDOUT,$header,length($header));
 $finished = ( 1==0 );
 while ( ! $finished ) {
   $got = sysread( PICFILE,$buff,100);
   if ( $got > 0 ) {
     syswrite( STDOUT,$buff,$got);
   }	
   if ( $got < 100) { 
     $finished = ( 1==1 );
   } 
 }
 close PICFILE;
 exit;
 

or:

 $picFile  = "..//imgheader001.jpg";
 $header   = "Content-Disposition: filename=ipstats2.jpg\nContent-Type: image/jpg\n\n";
 
 print $header;
 open my $in , '<:raw', $picFile ;
 binmode $in;
 my $buff;
 while ( read $in, $buff, 1000) {
   print $buff;
 }
 exit;
 

The PHP Script:

This script below saves to gbookFXXX.js file in the directory that the script is run in. XXX in the value passed up in form field guestbook.

/guestbook.php?guestbook=XXX

 <?php
 // Start the session - optional but could be used to check if logged in
 session_start(); // this seemed to log me out.
 ?>
 
 <?PHP
 //	guestbook.php - Guestbook script the saves the data wrapped in JavaScript function calls.
 // copyright 2002 douglas rice.
 // This script takes the data from the form fields.
 // and wraps it in a JavaScript Function Call:
 // gbF( "name","email",postedOn,IPaddress,"userfield1","userfield2","userfield3","userfield4","comments", ).
 
 // After appending the data the URL in form field: nextpage replaces the page.
 //
 // This uses the same form fields as the BTOpenWorld Guestbook.
 //
 
 
 
 $guestbook  = $_REQUEST["guestbook" ];
 $name 	    = $_REQUEST["name"];
 $email      = $_REQUEST["email"];
 $userfield1 = $_REQUEST["userfield1"];
 $userfield2 = $_REQUEST["userfield2"];
 $userfield3 = $_REQUEST["userfield3"];
 $userfield4 = $_REQUEST["userfield4"];
 $comments   = $_REQUEST["comments"];
 $nextpage   = $_REQUEST["nextpage"];
 
 
 function esc ( $s ) {
 // escape characters that are code to Java Script, and HTML
 // PHP escapes " and ' by default, so no need for call to 
 
 //$ss = addslashes ( $s );
 //Tip: This function can be used to prepare a string for storage in a database and database queries.
 
 //https://www.w3schools.com/php/func_string_addslashes.asp says
 //Note: Prior to PHP 5.4, the PHP dir magic_quotes_gpc was on by default 
 //and it ran addslashes() on all GET, POST, and COOKIE data by default. 
 //You should not use addslashes() on strings that have already been escaped, 
 //as it will cause double escaping. 
 //The function get_magic_quotes_gpc() can be used to check this.
 
 $ss = addslashes ( $s );
 //$ss = $s;
 // //CRLF and LF	//HTML specials
 
 $search = array ( "'\r'", "'\n'",	"'&'", "'<'", "'>'" );
 $replace = array ( "\\r\\\r", "",	"&amp;", "&lt;", "&gt;" );
 // Uses regular expressions to remove 
 return preg_replace ($search, $replace, $ss );
 
 }
 
 $date=gmstrftime ("%d %b %Y %H:%M:%S");
 
 // append to the file gbookF + the name specified by $guestbook + .js
 $fp = fopen ("./gbookF$guestbook.js", "a+b") or die("Unable to open file!"); 
 
 fputs($fp,"gbF(\n");
 
 fputs($fp,"'".esc( $name )."',\n '".esc( $email )."',\n '$date',\n '".getenv ( "REMOTE_ADDR" )."',\n");
 
 // output the 4 user fields and comments
 fputs($fp,"'".esc( $userfield1 )."',\n '".esc( $userfield2 )."',\n");
 fputs($fp,"'".esc( $userfield3 )."',\n '".esc( $userfield4 )."',\n");
 fputs($fp,"'".esc( $comments )."'\n");
 fputs($fp,");\n\n");
 
 fclose($fp);
 
 // reload the page with the URL provided in next page.
 header("Location: $nextpage\n\n\n" );
 
 //WARNING 
 // I had an idea to use HTTP_REFERER and append ?ok to return to the calling page and add an exit code.
 //
 // if I called 192.168.1.x/gb.php there is no HTTP_REFERER and I got too many redirects
 // - Edge does not like Location: ?ok 
 //
 
 // I tried adding ?ok but did not have a valid HTTP_REFERER 
 // This and spun EDGE and filled up my file gbookF.js and I ran out of disk Quota 
 //header("Location: ?ok\n\n\n" );
 
 //exit; // I added  the sessions code, but this exit caused the session to log out. PHP needs to tidy up SESSION.
 ?>
 
 

Copy the code above into a file called guestbook.php and upload to your web server where you can run php pages.

I upload mine to /demo . You then need to point the guestbook form action to this PHP page.

<form name="F1" action="/demo/guestbook.php" method="POST" >
 <input type="text" name="guestbook" size="20" value="XXX" >
 </form>

Upload a file called gbookFXXX.js to /demo and make the file permissions writable.

Include the file: gbookFXXX.js in your web page using :

<!-- remote guestbook-->
<script> 
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments)
 { // YOU WRITE THE BODY OF THIS FUNCTION TO EXTRACT THE DATA IN YOU GUEST BOOK
 self.document.write( name+ ","+ email+","+ postedOn+","+ IPaddress+ "<BR>"+ 
  userfield1+","+ userfield2+","+userfield3+","+ userfield4+","+comments+"<P>" ) 
 } </script>
<!-- Include remote guestbook-->
 <script src = "/demo/gbookFXXX.js" language = "JavaScript" type = "text/javascript" >
</script>

The PHP Script loads the data serverside and downloads it inline.

An alternative to the cache busting code above is to use PHP server side, possibly with password controlled login.

 <!--
 <script>
 now = new Date()
 /* try some cache busting append the time to the URL to make it different, so the URL is not cached. */
 
 self.document.write( '<scr'+'ipt src=https://www.adastralsailing.org.uk/apsc/gb/gbookFBookings_.js?'+now.getTime()+'  language="JavaScript"> </scr'+'ipt>' )
 
 </script>
 -->
 
 <script>
 <?php
 // only include data if logged in.
 
 if ( $_SESSION["loggedin"] === "loggedin"){
   // Read a file into a string
   $str = file_get_contents("gb/gbookFBookings_.js");
 
   // Check for errors
   if ($str === false) {
   // Handle error
   echo "Error reading file";
   } else {
   // Print the file contents
   echo $str;
   }
 }  
 ?>
 </script>
 

Using gbF() serverside

It is possible to use the idea in the PHP script, but the security MUST be considered

 <?php
 
 // Global Array with imported data
 $gb=array();
 
 function gbF( $name,$email,$postedOn,$IPaddress,$userfield1,$userfield2,$userfield3,$userfield4,$comments){
 
   global $gb;
    
    // load object 
    $r = array();
    
    // adjust to requirements.
    $r["name"]       = $name;
    $r["email"]      = $email;
    $r["postedOn"]   = $postedOn;
    $r["IPaddress"]  = $IPaddress;
    $r["userfield1"] = $userfield1;
    $r["userfield1"] = $userfield2;
    $r["userfield1"] = $userfield3;
    $r["userfield1"] = $userfield4;
    $r["comments"]   = $comments;
 
    // append object.
    $gb[ count($gb) ]=$r;
 
    // append object using name as index, and add count.
    $gb[ "$name".count($gb) ]=$r;
 
 
    // supply your own code to filter and load the data.   
    echo "==";
    echo $name."\n";
    echo $postedOn."\n";
 };
     
 ?>
 
 
 <?php
 // use gbF() to load data.
 gbF(
 'Doug Rice',
  'ok@ok.com',
  '27 Mar 2024 08:04:53',
  '212.56.108.219',
 'book|2024_Mar_5|day|trailer|other|6.50|ref:#938806|',
  'skipper,crewList',
 '',
  '',
 ''
 );
 
 gbF(
 'Doug Rice',
  'ok@ok.com',
  '27 Mar 2024 17:53:49',
  '212.56.108.219',
 'book|2024_Mar_4|am|Hartley|private|10.16|ref:#921593|',
  'skipper,crewList',
 '',
  '',
 ''
 );
 
 
 // helper functions to dump and sort the data
 echo "\n=============================\n";
 json_encode( $gb, JSON_PRETTY_PRINT );
 
 
 echo "\n=============================\n";
 var_dump( $gb );	//Dumps information about one or more variables
 
 echo "\n=============================\n";
 var_export( $gb );
 
 echo "\n== ksort ===========================\n";
 ksort( $gb );
 var_dump( $gb );	//Dumps information about one or more variables
 echo "\n=============================\n";
 
 
 ?>
 
 
 
 
 

The code below loads from a file, but it does not call the PHP gbF() unless the file has the PHP tags <?php ?> inside it.

It is possible to get the guestbook script to wrap the php tags around each gbF() so you can still append each entry to the file.

 
 <?php require 'gb/gbookFBookings_.js';
 ?>
 
 

Using php allows SESSION variables to be used server side to control user access

see https://www.w3schools.com/PHP/php_sessions.asp


The ASP Script:

This script below saves to gbookFXXX.js file in the directory that the script is run in. XXX in the value passed up in form field guestbook.
<% @ LANGUAGE="VBSCRIPT" %>
 <%
  OPTION EXPLICIT
  DIM L_Guestbook, count, CursorType, intMID,objConn ,rst, strProvider, strQuery, StrSort
 
 ' NOTE: This script escapes   ' " \ CR or LF which would corrupt JavaScript.
 
 
    Function strip( ByVal s)
      s = replace(s,chr( 92 ),Chr( 92 ) & Chr( 92 )) ' first double up any \
 
      s = replace(s,chr( 39 ),Chr( 92 ) & Chr( 39 )) ' now escape ' 
      s = replace(s,chr( 34 ),Chr( 92 ) & Chr( 34 )) ' now escape "
 
      ' Not really sure about how to escape < > &lt; and &gt;
      ' s = replace(s,"<","&lt;") ' now escape <
      ' s = replace(s,">","&gt;") ' now escape >
 
 	 ' now replace CR LF and LF  
      s = replace(s,vbCr,"")
      s = replace(s,vbLf,"<BR>\"+vbCr+vbLf)
 
      strip = s
      
    End Function
 
 
     Const ForReading = 1, ForWriting = 2, ForAppending = 8
 
     Const TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0
 
     Dim fs, f, ts, s, gbook
     
     Set fs = CreateObject("Scripting.FileSystemObject")
 
     gbook = server.mappath("gbookF"& request.Form("guestbook")&".js")
 
     if (  not fs.FileExists( gbook ) ) Then
 		fs.CreateTextFile gbook             'Create a file
     
     End If
 
     Set ts = fs.OpenTextFile( gbook, ForAppending,TristateFalse)
 
 ' gbF(
 '  "name",
 '  "email",
 '  " Mon 5/Nov/2001 at 18: 3: 37 ", ' date script run
 '  "132.146.89.88",
 '  "userfield1",
 '  "userfield2",
 '  "userfield3",
 '  "userfield4",
 '  ""
 '  );
 
     ts.WriteLine( "gbF(  ")
     ts.WriteLine( "'" & strip (  request.Form("name")       ) & "',")
     ts.WriteLine( "'" & strip (  request.Form("email")      ) & "',")
     ts.WriteLine( "'" & now                        & "',")
     ts.WriteLine( "'" & Request.ServerVariables("REMOTE_ADDR")  & "',") 
     ts.WriteLine( "'" & strip (  request.Form("userfield1") ) & "',")
     ts.WriteLine( "'" & strip (  request.Form("userfield2") ) & "',")
     ts.WriteLine( "'" & strip (  request.Form("userfield3") ) & "',")
     ts.WriteLine( "'" & strip (  request.Form("userfield4") ) & "',")
 
     ts.WriteLine( "'" & strip (  request.Form("comments")   ) & "'")
     ts.WriteLine( ");" )
     ts.WriteLine( " " )
 
     ts.Close
 
     response.redirect request.Form("nextpage")
 
 %>
 
 

The Form:

Cut and paste the code below into a file called form.htm, and save to your desktop and open form.htm.

When you press the submit button it append the form fields to:

/gb/gbookFXXX.js 

using:

 action="/cgi-bin/guestbook.pl"

Change the action to point to your CGI script.

( You can try this out using my guestbook /gb/gbookFXXX.js for now! )

 

<html>
 <head>
 <title>Simple Form</title>
 <link rel="stylesheet" href="../club.css">
 </head>
 <body>
 <H2>Minimal Form</h2>
 <!-- select which server script you are going to use.
 <form name="F1" action="guestbook.asp" method="POST" >
 <form name="F1" action="guestbook.php" method="POST" >
 -->
 <form name="F1" action="/cgi-bin/guestbook.pl"        method="POST" >
 <input type="text" name="name" value="name" size="20" > :name<BR>
 <input type="text" name="email" value="email" size="20" > :email<BR>
 
 <input type="text" name="userfield1" value="userfield1" size="20" > :userfield1<BR>
 <input type="text" name="userfield2" value="userfield2" size="20" > :userfield2<BR> 
 <input type="text" name="userfield3" value="userfield3" size="20" > :userfield3<BR>
 <input type="text" name="userfield4" value="userfield4" size="20" > :userfield4<BR>
 
 Please enter any comments:-
 <textarea name="comments" rows="2" cols="80"></textarea>
 
 <HR>
 Control Fields (Normally hidden):-<BR>
 <input type="text" name="guestbook" size="20" value="XXX" onFocus="focusB1()" >guestbook. The form fields are appended to gbookFXXX.js<BR>
 <input type="text" name="nextpage" size="70" value="/www/hp/gbbook/simple/codeDump.htm" onFocus="focusB1()" >nextpage - replaces this form<BR>
 <input type="text" name="loadnextpage" size="70" value="loadnextpage" onFocus="focusB1()" >loadnextpage - flag <BR>
 <input type="submit" name="B1" value="Submit">
 </form>
 The form above appends the form fields to gbookFXXX.js formatted using CGI Script guestbook.pl or guestbook.asp
 <pre>
 gbF(
 "name",
 "email",
 " Mon 5/Nov/2001 at 18: 3: 37 ", ' date script run
 "132.146.89.88",
 "userfield1",
 "userfield2",
 "userfield3",
 "userfield4",
 "comments"
 );
 </pre>
 </body>
 </html>
 

Extras: The following code is required to make the pages run smoothly.

 

Download, Sort and Select Data

// function call back for data stored in guestbook.
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
   gbA[ count ] = new GB( name,email,postedOn,IPaddress,
                          userfield1,userfield2,userfield3,userfield4, comments 
                         );
   count++
 }
 
 // Function used by sort.
 function sortCallbackFn( a, b ) {
 
   if ( a.userfield1 != b.userfield1 ) {
     if ( a.userfield1 > b.userfield1 ){ return 1 }
     if ( a.userfield1 < b.userfield1 ){ return -1 }
     return 1
   }
   return ( b.count*1.0 - a.count*1.0 )
 }
 
 </script>
 
 <h4> Guestbook data, as loaded </h4>
 <script> loading	= "./gbookFXXX.js" </script>
 <script language = "JavaScript" src = "./gbookFXXX.js" type = "text/javascript"> </script>
 
 <script> loading	= "/gb/gbookFXXX.js" </script>
 <script language = "JavaScript" src = "/gb/gbookFXXX.js" type = "text/javascript"> </script>
 
 <h4> Now Sort the data by Userfield1, then reversed submit order </h4>
 
 <script>
 gbA.sort( sortCallbackFn )
 </script>
 

Display Data in a Table.

opStr ="<table>"
for( I=0; I< gbA.length ; I++ ){
 
   opStr = opStr+
     "<tr><td><B><A href='javascript:pop("+I+")'>"+ gbA[ I ].name +"</A></B>"+"<BR>"+gbA[ I ].email+
     "<br>" + gbA[ I ].postedOn+
     "</td><td>"	+ gbA[ I ].userfield1+
     "</td><td>" + gbA[ I ].userfield2+
     "</td><td>" + gbA[ I ].userfield3+
     "</td><td>" + gbA[ I ].userfield4+
     "</td></tr>" +
     "<tr><td colspan=5>"+ gbA[ I ].comments +
     "</td></tr>"
 
   }
   opStr ="</table>"
 
   self.document.write( opStr )
 

 

Force a Reload of updated data. example form

<form name="F1" action="guestbook.asp" method="POST" onSubmit="return validate()">
   <input type="text" name="name" value="name" size="20" > :name - set up by cookie<BR>
   <input type="text" name="email" value="email" size="20" > :email - set up by cookie<BR>
 </form>
<script>
 // for the session iis sends first cookie = to what was assignd to document.cookie
 // However IIS sends up anothe cookie for the ASP script, so split on "; " 
 
 window.status = document.cookie
 
 // split cookies
 var cookiesA = document.cookie.split("; ") // split on ; and space
 var cookieA = new Array()
 // set up defaults 
 cookieA[ "name" ] = "name"
 cookieA[ "email" ] = ""
 
 // break out the cookies into an associative array index by the string. 
 for ( I=0 ; I < cookiesA.length ; I++ ){
   nvA = cookiesA[ I ].split("=")
   cookieA[ nvA[0] ] = nvA[1]
   //alert( nvA[0]+"="+cookieA[ nvA[0] ] )
 }
 
 if ( cookieA[ "reload" ] == "needed" ){
   document.cookie="reload=done"
   alert("reload needed ... reloading")
   document.location.reload( 1==1 )
 }
var F1 = window.document.forms["F1"]
 // This is picked up from a cookie
 F1.name.value = cookieA[ 'name' ]
 F1.email.value = cookieA[ 'email' ]
 
 
function validate(){
 var F1 = window.document.forms["F1"]
   if ( ( F1.name.value == "name" ) || ( F1.name.value == "undefined" ) ) {
     alert(' Please change the name and email address..')
     F1.name.focus()
     F1.name.select()
     return false
   }
   // 
   document.cookie="name="+ F1.name.value  + "; expires=" + expiry.toGMTString()
   document.cookie="email="+F1.email.value + "; expires=" + expiry.toGMTString()
   
   document.cookie="reload=needed"
   return true
 }
 </script>
 

Form Populate - part fill the form.

function pop( gbA_row ){
   // pict up the data from the sorted array and fill in the update form.
   var F1 = window.document.forms["F1"]
   var record = gbA[ gbA_row ]	
 
   // This is picked up from a cookie
   F1.name.value = cookieA[ 'name' ]
   F1.email.value = cookieA[ 'email' ]
 
   F1.userfield1.value = record.userfield1
   F1.userfield2.value = record.userfield2
   F1.userfield3.value = record.userfield3
   F1.userfield4.value = record.userfield4
   F1.comments.value = record.comments.replace( /<BR>/g,"\n" )
   // focus on end and start of file to display form on screen.
   F1.comments.focus()
   F1.name.focus()
 }
 

 

The Perl Script Extras - date, credits and password:

 #
 # setDate setup $date to string that javascript can parse using:  now= new Date( postedOn )
 #
 
 sub setDate {
 
 # Get the Date for Entry
 # output date so that the web pages can be use the javascript:  now= new Date( postedOn )
 
 #IE 5 formats toUTCString():		Tue, 25 Jun 2002 05:47:32 UTC
 #Netscape Navigator toUTCString():	Tue, 25 Jun 2002 05:49:59 GMT
 
 #($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);		   
 #  0     1     2    3     4     5    6     7     8
 @t = localtime();
 @days=  ('Sun','Mon','Tue','Wed','Thu','Fri','Sat','Sun');
 @months=('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
 $fullYear=1900+$t[5];
 #toUTCString():		#Tue, 25 Jun 2002 05:47:32 UTC
 $date = "$days[$t[6]], $t[3] $months[$t[4]] $fullYear ".substr("0".$t[2],-2,2).":".substr("0".$t[1],-2,2).":".substr("0".$t[0],-2,2)." UTC+0100";
 
 }
 
 
 #-----------------------------------------------------------------
 # Check the credits
 sub checkCreditLevel {
   $creditfile    = $ENV{'DOCUMENT_ROOT'}.'/credits.js';
 
   open (CREDITFILE,$creditfile ) || &file_error( CREDITFILE,$creditfile ); 
 
   @CREDIT_LINES=<CREDITFILE>;
   close(CREDITFILE);
   $CREDIT_SIZE=@CREDIT_LINES;
 
   foreach $memRow (@CREDIT_LINES){
    ($name, $value)  = split(/=/, $memRow);
    #print "$name, $value<P>\n";
    $CREDITSA{$name} = $value;
   }
 
   # if blank create
   if ( $CREDITSA{'credits'} eq '' ) {
     $CREDITSA{'credits'}=1000;
   }
 
   $credits = $CREDITSA{'credits'}-1;
   #print "This guestbook server has $credits credits left<P>\n";
 
   # if fraudlently set to -ve value
   if ( $credits > 1000 ) {
     $credits=1000;
   }
 
   open (CREDITFILE,">$creditfile" ) || &file_error( CREDITFILE,$creditfile ); 
   print CREDITFILE "credits=$credits\n";
   close(CREDITFILE);
 
   if ( $credits < 0  ) {
     #print "<h1> Credits run out, data not appended  </h1>";
 
     exit;
   }
 } 
 #-----------------------------------------------------
 
 # Password protect submission
 
 # read passwords into associative array, but only if password equal to old password
 
 # store the passwords  in a file called password.txt in $ENV{'DOCUMENT_ROOT'}
 #each line formatted
 #
 #guestbook,password,oldpassword,check
 #
 # new passwords can be appended and are only changed is old password matches e.g
 
 #Bug,newpassword,,check
 #Bug,newpassword2,newpassword,check
 #AotherBook,password,,check
 
 
 
 #GBOOK=password
 # Get Membership details 
 
 sub checkPassword {
   $passwordfile  = $ENV{'DOCUMENT_ROOT'}.'/password.txt';
   if ( -e $passwordfile ) {
     open (PASSWORDFILE,$passwordfile ) ||     &file_error( PASSWORDFILE, $passwordfile ); 
     #    die "Can't Open $passwordfile: $!\n";
 
     @PASSWORD_LINES=<PASSWORDFILE>;
     close(PASSWORDFILE);
     $PASSWORD_SIZE=@PASSWORD_LINES;
 
     foreach $memRow (@PASSWORD_LINES){
       ($guestbook, $password,$oldpassword,$check) = split(/,/, $memRow);
       # only update password if oldpassword is a valid password.
       if ( $oldpassword eq $PASSWORDSA{$guestbook} ){
         $PASSWORDSA{$guestbook} = $password;
       } 
       if ( "1" eq "2" ) {
          # Debug output to test Updating password list
         print "Content-Type: text/html\n\n";
         print "<html><head><title>Password problem</title></head><body>\n";
         #print "<h1>Old password :$oldpassword: for :$guestbook: not found: </h1>\n";
         print "<h1>looking for: $PASSWORDSA{$guestbook}</h1>\n";
         print '</body></htmr>\n';
         exit;
       }
     }
 
     $password      = $FORMJS{'password'};
     $guestbook     = $FORMJS{'guestbook'};
     $validpassword = $PASSWORDSA{$guestbook};
 
     #&file_error( CREDITFILE,$creditfile."|$password|$guestbook|$validpassword|" ); 
 
     if ( $password ne $validpassword )  {
  
       $PASSWORDSA{$name} = $value;
 
       # Print Beginning of HTML
       print "Content-Type: text/html\n\n";
       print "<html><head><title>Thank You</title></head><body>\n";
       print "<h1>INVALID PASSWORD for guestbook:$guestbook: </h1>\n";
       #print "<h1> wanted:$validpassword: got :$password: </h1>\n";
       foreach $memRow (@PASSWORD_LINES){
         ($name, $value) = split(/,/, $memRow);
         #print "<body><h1>PASSWORDS :$PASSWORDSA{$name}:$name:$value:, press back </h1>\n";
 
         $PASSWORDSA{$name} = $value;
       }
       #print "<body><h1>Wanted :$validpassword:, press back </h1>\n";
       print "<body><h1>Your data has not been added, press back </h1>\n";
       print "<H1><A HREF=$FORM{'nextpage'} target=_top > return... <A></H1>";
       print '</body></htmr>\n';
       exit;
     }
   }
 }

 


Sorted Guestbook, with update form :

<html>
 <head>
 <title>Sorted Guestbook, with update form</title>
 <link rel="stylesheet" href="../club.css">
 </head>
 <body>
 <h1>Sorted Guestbook, with update form</h1>
 <script>
 
 // define array to store guestbook data in.
 var count          = 0
 var gbA            = new Array()
 var loading		   = ""
 
 // function adds data to an array.
 function GB( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
 	this.name       = name
 	this.email      = email
 	this.postedOn   = postedOn
 	this.IPaddress  = IPaddress
 	this.userfield1 = userfield1
 	this.userfield2 = userfield2
 	this.userfield3 = userfield3
 	this.userfield4 = userfield4
 	this.comments   = comments
 	
     // add extra fields, as data is loaded
 	this.count      = count
 	lastUpdated     = name+", "+email+" at "+postedOn+" updating "+ userfield4 + ":" + userfield1 
 	// sort Key for sort on dates
 	var date = new Date( userfield1 )
 	if ( isNaN( date ) ) {
 	  if ( isNaN( userfield1 ) ) {
 	    date = new Date( "1 aug 3000")
 	    this.date = date
 	    this.sortKey = date.valueOf()
 	  } else {
 	    this.sortKey = userfield1*1.0	
 	  }
 	} else {
 	  this.date = date
 	  this.sortKey = date.valueOf()
 	}
 	return this
 }
 
 // function call back for data stored in guestbook.
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
     gbA[ count ] = new GB(  
         name,email,postedOn,IPaddress,
         userfield1,userfield2,userfield3,userfield4,
         comments
 	);
     count++
 
     // display data as it is loaded - remove 
     opStr = 
     "<PRE>"+
            
 	  "<PRE>"+
 	  
 	  "gbF( //from: "+loading+"\n"+
 	  "  \""+unescape(name)+"\","+
 	  "  \""+unescape(email)+"\","+
 	  "  \""+unescape(postedOn)+"\","+
 	  "  \""+unescape(IPaddress)+"\",\n"+
 	  "  \""+unescape(userfield1)+"\","+
 	  "  \""+unescape(userfield2)+"\","+
 	  "  \""+unescape(userfield3)+"\","+
 	  "  \""+unescape(userfield4)+"\",\n"+
 	  "  \""+comments.replace(/<BR>/gi,"&lt;BR&gt;\\")+"\"\n"+
 	  "  );\n"+
 	  "" + "</PRE>" 
 
 
     // self.document.write( opStr )
 
 }
 
 // Function used by sort.
 function sortCallbackFn( a, b ) {
 
   if ( ( a.sortKey - b.sortKey ) != 0 ) {
     return ( a.sortKey - b.sortKey )
   }
 
   if ( a.userfield1 != b.userfield1 ) {
     if ( a.userfield1 > b.userfield1 ){ return  1 }
     if ( a.userfield1 < b.userfield1 ){ return -1 }
     return 1
   }
   
   return ( b.count*1.0 - a.count*1.0 )
 }
 </script>
 
 <script> loading	= "/gb/gbookFXXX.js" </script>
 <script  language = "JavaScript"  src = "/gb/gbookFXXX.js"  type = "text/javascript">  </script>
 
 <h4> Now display Guestbook data sorted alphabetically by Userfield1, then reversed submit order  </h4>
 
 <script>
    gbA.sort( sortCallbackFn )
 </script>
 
 <table border=2 >
 <script>
 
  
  var opStr = "<tr><th><B><A href=''> link +</A></B><BR>email<br>postedOn"+
 	"</th><th>userfield1"+
 	"</th><th>userfield2"+
 	"</th><th>userfield3"+
 	"</th><th>userfield4"+
 	"</th></tr>" +
 	"<tr><th colspan=5>comments" +
 	"</th></tr>"
 
 
 
  for( I=0; I< gbA.length ; I++ ){
  	
    opStr = opStr+
    		"<tr><td><B><A href='javascript:pop("+I+")'>"+  gbA[ I ].name +"</A></B>"+"<BR>"+gbA[ I ].email+
    		"<br>"   + 	gbA[ I ].postedOn+
    		"</td><td>"	 +	gbA[ I ].userfield1+
    		"</td><td>"  +  gbA[ I ].userfield2+
    		"</td><td>"  +  gbA[ I ].userfield3+
    		"</td><td>"  +  gbA[ I ].userfield4+
    		"</td></tr>" +
 //   		"<tr><td colspan=5><PRE>"+ gbA[ I ].comments+"</td></tr>"
 		// print out comments, convert <BR> to \n so that copy works.
    		"<tr><td colspan=5 class=code ><PRE>"+ gbA[ I ].comments.replace(/<BR>/gi,"\n") + "</PRE></TD></TR>"+
 		""
    		"</td></tr>"
    		
  }
 
  self.document.write( opStr )
 
 
 // pick up cookies for reload, and name and email
   var today  = new Date();
   var expiry = new Date(today.getTime() + 365 * 24 * 60 * 60 * 1000);
   //var expiry = new Date(today.getTime() +   1 *  1 *  5 * 60 * 1000);
 
   // for the session iis sends first cookie = to what was assignd to document.cookie
   // However IIS sends up anothe cookie for the ASP script, so split on "; " 
   
   window.status = document.cookie
   
   // split cookies
   var cookiesA = document.cookie.split("; ")  // split on ; and space
   var cookieA  = new Array()
   // set up defaults 
   cookieA[ "name" ] = "name"
   cookieA[ "email" ] = ""
 
   // break out the cookies into an associative array index by the string.  	
   for ( I=0 ; I < cookiesA.length ; I++ ){
     nvA = cookiesA[ I ].split("=")
     cookieA[ nvA[0] ] = nvA[1]
     //alert( nvA[0]+"="+cookieA[ nvA[0] ] )
   }
 
   if ( cookieA[ "reload" ] == "needed" ){
   	document.cookie="reload=done"
   	alert("reload needed ... reloading")
   	document.location.reload( 1==1 )
   }
 
 
  function pop( gbA_row ){
    // pict up the data from the assorted file and fill in the update form.
    var F1 = window.document.forms["F1"]
    var record = gbA[ gbA_row ]	 
 
    // This is picked up from a cookie
    F1.name.value       =  cookieA[ 'name'  ]
    F1.email.value      =  cookieA[ 'email' ]
 
    F1.userfield1.value =  record.userfield1
    F1.userfield2.value =  record.userfield2
    F1.userfield3.value =  record.userfield3
    F1.userfield4.value =  record.userfield4
    F1.comments.value   =  record.comments.replace( /<BR>/gi,"\n" )
    // focus on end and start of file to display form on screen.
    F1.comments.focus()
    F1.name.focus()
   }
 
 
   function validate(){
     var F1 = window.document.forms["F1"]
 
     if ( ( F1.name.value == "name" ) || ( F1.name.value == "undefined" ) ) {
       alert(' Please change the name and email address..')
       F1.name.focus()
       F1.name.select()
       return false
     }
     // 
     document.cookie="name="+F1.name.value   + ";expires=" + expiry.toGMTString()
     document.cookie="email="+F1.email.value + ";expires=" + expiry.toGMTString()
 
     document.cookie="reload=needed"
     return true
   }
 
 </script> 
 
 </table>
 <h4> Update form </h4>
 <script>
 self.document.write( "Cookies: "+document.cookie +"<p>" )
 </script>
 
 
 Click of the name above to use existing data, or edit the data below:-<BR>
 <!--
 <form name="F1" action="/gb/guestbook.php" method="POST" onSubmit="return validate()">
 -->
 <form name="F1" action="/cgi-bin/guestbook.pl" method="POST" onSubmit="return validate()">
 <input type="text" name="name" value="name" size="20" > :name - set up by cookie<BR>
 <input type="text" name="email" value="email" size="20" > :email - set up by cookie<BR>
 
 If Userfield1 is a valid date / time then sort as date else if a valid number sort as a number, else sort alphabetically, then reversed submit order<BR>
 
 <input type="text" name="userfield1" value="userfield1" size="20" > :userfield1 ( valid date is dd mmm yyyy hh:mm:ss )<P>
 
 
 <input type="text" name="userfield2" value="userfield2" size="20" > :userfield2<BR> 
 <input type="text" name="userfield3" value="userfield3" size="20" > :userfield3<BR>
 <input type="text" name="userfield4" value="userfield4" size="20" > :userfield4<BR>
 
 Please enter any comments:-<BR>
 <textarea name="comments" rows="5" cols="80"></textarea>:comments<BR>
 
 <HR>
 Control Fields (Normally hidden):-<BR>
 <input type="text" name="guestbook" size="20" value="XXX" onFocus="focusB1()" >guestbook. The form fields are appended to gbookFXXX.js<BR>
 <input type="text" name="nextpage" size="50" value="/www/hp/gbbook/simple/codeSorted.htm" onFocus="focusB1()" >nextpage - replaces this form<BR>
 <input type="text" name="loadnextpage" size="50" value="loadnextpage" onFocus="focusB1()" >loadnextpage - flag <BR>
 <input type="submit" name="B1" value="Submit -using guestbook.pl  on apache based cgi.dougrice.plus.com" onClick="submitPl()">
 <input type="submit" name="B2" value="Submit -using guestbook.php on apache based cgi.dougrice.plus.com" onClick="submitPl2()">
 
 <A href="/cgi-bin/gb.pl" > env </A>
 
 <script>
   // add a button to allow user to use an Active-X function to write the form to a local file
   if ( self.location.protocol == "file:" ){
     self.document.write('<BR>If running IE5.5 locally use Active - X to :<BR><input type="Button" name="B4" value="Append to local file: gbookFXXX.js" onClick="javascript:AXopFile(\'F1\')">')
   }
 </script>
 
 
 
 </form>
 
 <script>
 
 
    var F1 = window.document.forms["F1"]
    // This is picked up from a cookie
    F1.name.value       =  cookieA[ 'name'  ]
    F1.email.value      =  cookieA[ 'email' ]
 
    // if running locally
    if ( self.location.protocol == "file:" ){
      var s = self.location.pathname 
      var s1 = s.replace( /codeSorted.htm/g,"gbookF"+F1.guestbook.value+".js" )  	// remove file name
      F1.B4.value = " Add to the local disk file: " + s1
    }
 
 
   function submitPl( formName ){
     validate();
     var f = window.document.forms[ "F1" ]   
     F1.action="/cgi-bin/guestbook.pl"
     //F1.action="http://localhost/cgi-bin/guestbook.pl"
     F1.target="popup"
     //F1.nextpage.value=location
     //F1.submit()
     return ( 1==1 )
   }
 
   function submitPl2( formName ){
     validate();
     var f = window.document.forms[ "F1" ]   
     F1.action="/gb/guestbook.php"
 
     F1.target="popup"
     //F1.nextpage.value=location
     //F1.submit()
     return ( 1==1 )
   }
 
 
 
 function strip ( s ) {
 s= s.replace( /\\/g,"\\\\")
 s= s.replace( /\"/g,"\\\"")
 s= s.replace( /\'/g,"\\\'")
 s= s.replace( /\n/g,"<BR>\\n")
 s= s.replace( /\r/g,"")
 return s
 }
 
 function focusB1( formName ){
 var f = window.document.forms[ "F1" ]
 f.B1.focus()
 }
 
 
 function AXopFile( formName ){
 // Function that uses Active X to emulate remote ASP server
 var f = window.document.forms[ "F1" ] 
 var fs,s;
 var now = new Date()
 var s
 validate();
 s = self.location.pathname	// gives /D:\data\hp\gbbook\DHRsorted.htm
 s = s.replace( /\//g,"") // remove leading /
 s = s.replace( /codeSorted.htm/g,"gbookF"+f.guestbook.value+".js" ) // remove file name
 gbook=s
 alert( "Add to local File:\n"+gbook )
 
 var fs = new ActiveXObject("Scripting.FileSystemObject");
 
 if ( ! fs.FileExists( gbook ) ) {
 fs.CreateTextFile( gbook )
 }
 
 var ts = fs.OpenTextFile( gbook ,8 , 0 );
 
 ts.WriteLine("//Appended using Active X.");
 
 ts.WriteLine( "gbF( ")
 ts.WriteLine( "'" + strip ( f.name.value ) + "',")
 ts.WriteLine( "'" + strip ( f.email.value ) + "',")
 ts.WriteLine( "'" + now + "',")
 ts.WriteLine( "'127.0.0.1'," )
 ts.WriteLine( "'" + strip ( f.userfield1.value ) + "',")
 ts.WriteLine( "'" + strip ( f.userfield2.value ) + "',")
 ts.WriteLine( "'" + strip ( f.userfield3.value ) + "',")
 ts.WriteLine( "'" + strip ( f.userfield4.value ) + "',")
 ts.WriteLine( "'" + strip ( f.comments.value ) + "'" )
 ts.WriteLine( ");" )
 ts.WriteLine( " " )
 
 ts.Close();
 
 location.reload( 1==1 )
 }
 
 </script>
 </body>
 </html>
 

Sorted (Numerically / alphabetically ) Guestbook, with update form :

<html>
 <title>Sorted Guestbook, with update form</title>
 <head>
 <link rel="stylesheet" href="../club.css">
 </head>
 
 <body>
 <h1>Sorted Guestbook, with update form</h1>
 <script>
 
 // define array to store guestbook data in.
 var count = 0
 var gbA = new Array()
 var loading	= ""
 var lastUserfield1=""
 
 // function adds data to an array.
 function GB( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
   this.name = name
   this.email = email
   this.postedOn = postedOn
   this.IPaddress = IPaddress
   this.userfield1 = userfield1
   this.userfield2 = userfield2
   this.userfield3 = userfield3
   this.userfield4 = userfield4
   this.comments = comments
 
   // add extra fields, as data is loaded
   this.count = count
 
   // sort Key for sort on numeric then dates the alphabetic
   var date = new Date( userfield1 )
   this.sortKeyAlpha = ""
   // sortKeyType is sorted first and are A,D,N, use lower case to sort after Upper case	
   // if you want numbers,alpha,dates use types a,d,N as N < a < d
   if ( isNaN( date ) ) {
     // not a date
     if ( isNaN( userfield1 ) ) {
       // alphabetic
       this.date = new Date( "1 aug 3000" )
       this.sortKeyType="a" 
       this.sortKey  = 0.0 
       this.sortKeyAlpha = userfield1.toUpperCase()
     } else {
       //numeric
       this.sortKeyType="N"
       this.date = new Date( "1 aug 3000" )
       this.sortKey = userfield1*1.0
     }
   } else {
     //date
     this.sortKeyType="d"
     this.sortKey = date.valueOf()
   }
 
   lastUpdated = name+", "+email+" at "+postedOn+" updating "+ userfield4 + ":" + userfield1 
   return this
 }
 
 // function call back for data stored in guestbook.
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
   gbA[ count ] = new GB( 
     name,email,postedOn,IPaddress,
     userfield1,userfield2,userfield3,userfield4,
     comments
   );
   count++
 }
   // Function used by sort.
 
 function sortCallbackFn( a, b ) {
   if ( a.userfield1 != b.userfield1 ) {
     if ( a.userfield1 > b.userfield1 ){ return 1 }
     if ( a.userfield1 < b.userfield1 ){ return -1 }
     return 1
   }
   return ( b.count*1.0 - a.count*1.0 )
 }
 
 // Function used by sort.
 function sortCallbackFn2( a, b ) {
   // sort into Alpha,Dates,Numbers first.
   if ( a.sortKeyType != b.sortKeyType ) { 
     if ( a.sortKeyType > b.sortKeyType ) {
       return 1
     }  
     return -1
   } 
   // item sorted down to Alpha,Date,Number
   if ( a.sortKey*1.0 == b.sortKey*1.0 ){
     if ( a.sortKeyAlpha == b.sortKeyAlpha ) {
       return -( a.count*1.0 - b.count*1.0 )
     } 
     if ( a.sortKeyAlpha > b.sortKeyAlpha ) {
       return 1
     }  
     return -1
   } else {
     return ( a.sortKey*1.0 - b.sortKey*1.0 )  
   }
 }
 
 </script>
 
 <script> loading	= "./gbookFXXX.js" </script>
 <script language = "JavaScript" src = "./gbookFXXX.js" type = "text/javascript"> </script>
 
 <script> loading	= "/gb/gbookFXXX.js" </script>
 <script language = "JavaScript" src = "/gb/gbookFXXX.js" type = "text/javascript"> </script>
 
 Now display Guestbook data sorted by Userfield1 (ranked Number,alpha,date then sorted), then reversed submit order <P>
 <script>
   gbA.sort( sortCallbackFn2 )
 </script>
 
 <table border=2 >
 <script>
 var opStr = ""
 for( I=0; I< gbA.length ; I++ ){
 
   if ( lastUserfield1 != gbA[ I ].userfield1 ) {
     opStr = opStr+"<TR><TH colspan=5 >"+gbA[ I ].userfield1+"</TH></TR>"
   }
   lastUserfield1 = gbA[ I ].userfield1
   opStr = opStr+
     "<tr><td><B><A href='javascript:pop("+I+")'>"+ gbA[ I ].name +"</A></B>"+"<BR>"+gbA[ I ].email+
     " ("+gbA[ I ].IPaddress+")"+
     "<br>" + gbA[ I ].postedOn+
     "</td><td>"	+ gbA[ I ].userfield1+
     "</td><td>" + gbA[ I ].userfield2+
     "</td><td>" + gbA[ I ].userfield3+
     "</td><td>" + gbA[ I ].userfield4+
     "</td></tr>" +
     "<tr><td colspan=5 class=code ><PRE>"+ gbA[ I ].comments.replace(/<BR>/gi,"\n") +"</pre></td></tr>"+
     ""
 
 }
 
 self.document.write( opStr )
 
 
 // pick up cookies for reload, and name and email
 var today = new Date();
 var expiry = new Date(today.getTime() + 365 * 24 * 60 * 60 * 1000);
 //var expiry = new Date(today.getTime() + 1 * 1 * 5 * 60 * 1000);
 
 // for the session iis sends first cookie = to what was assignd to document.cookie
 // However IIS sends up anothe cookie for the ASP script, so split on "; " 
 
 window.status = document.cookie
 
 // split cookies
 var cookiesA = document.cookie.split("; ") // split on ; and space
 var cookieA = new Array()
 // set up defaults 
 cookieA[ "name" ] = "name"
 cookieA[ "email" ] = ""
 
 // break out the cookies into an associative array index by the string. 
 for ( I=0 ; I < cookiesA.length ; I++ ){
   nvA = cookiesA[ I ].split("=")
   cookieA[ nvA[0] ] = nvA[1]
   //alert( nvA[0]+"="+cookieA[ nvA[0] ] )
 }
 
 if ( cookieA[ "reload" ] == "needed" ){
   document.cookie="reload=done"
   alert("reload needed ... reloading")
   document.location.reload( 1==1 )
 }
 
 
 function pop( gbA_row ){
   // pict up the data from the assorted file and fill in the update form.
   var F1 = window.document.forms["F1"]
   var record = gbA[ gbA_row ]	
 
   // This is picked up from a cookie
   F1.name.value = cookieA[ 'name' ]
   F1.email.value = cookieA[ 'email' ]
 
   F1.userfield1.value = record.userfield1
   F1.userfield2.value = record.userfield2
   F1.userfield3.value = record.userfield3
   F1.userfield4.value = record.userfield4
   F1.comments.value = record.comments.replace( /<BR>/g,"\n" )
   // focus on end and start of file to display form on screen.
   F1.comments.focus()
   F1.name.focus()
 }
 
 
 function validate(){
   var F1 = window.document.forms["F1"]
 
   if ( ( F1.name.value == "name" ) || ( F1.name.value == "undefined" ) ) {
     alert(' Please change the name and email address..')
     F1.name.focus()
     F1.name.select()
     return false
   }
   // 
   document.cookie="name="+ F1.name.value  + "; expires=" + expiry.toGMTString()
   document.cookie="email="+F1.email.value + "; expires=" + expiry.toGMTString()
 
   document.cookie="reload=needed"
   return true
 }
 </script> 
 
 </table>
 <h4> Update form </h4>
 
 Click of the name above to use existing data, or edit the data below:-<BR>
 <form name="F1" action="guestbook.asp" method="POST" onSubmit="return validate()">
 <input type="text" name="name" value="name" size="20" > :name - set up by cookie<BR>
 <input type="text" name="email" value="email" size="20" > :email - set up by cookie<BR>
 <input type="text" name="userfield1" value="userfield1" size="20" > :userfield1<BR>
 <input type="text" name="userfield2" value="userfield2" size="20" > :userfield2<BR> 
 <input type="text" name="userfield3" value="userfield3" size="20" > :userfield3<BR>
 <input type="text" name="userfield4" value="userfield4" size="20" > :userfield4<BR>
 
 Please enter any comments:-<BR>
 <textarea name="comments" rows="5" cols="80"></textarea>:comments<BR>
 
 <HR>
 Control Fields (Normally hidden):-<BR>
 <input type="text" name="guestbook" size="20" value="XXX" onFocus="focusB1()" >guestbook. The form fields are appended to gbookFXXX.js<BR>
 <input type="text" name="nextpage" size="50" value="/www/hp/gbbook/simple/codeSortedNA.htm" onFocus="focusB1()" >nextpage - replaces this form<BR>
 <input type="text" name="loadnextpage" size="50" value="loadnextpage" onFocus="focusB1()" >loadnextpage - flag <BR>
 <input type="submit" name="B2" value="Submit -using guestbook.pl on apache based dougrice.plus.com" onClick="javaScript:submitPl()">
 
 <script>
 // add a button to allow user to use an Active-X function to write the form to a local file
 if ( self.location.protocol == "file:" ){
   self.document.write('<BR>If running IE5.5 locally use Active - X to :<BR><input type="Button" name="B4" value="Append to local file: gbookFXXX.js" onClick="javascript:AXopFile(\'F1\')">')
 }
 </script>
 
 </form>
 
 <script>
 
 
 var F1 = window.document.forms["F1"]
 // This is picked up from a cookie
 F1.name.value = cookieA[ 'name' ]
 F1.email.value = cookieA[ 'email' ]
 
 // if running locally
 if ( self.location.protocol == "file:" ){
   var s = self.location.pathname 
   var s1 = s.replace( /codeSorted.htm/g,"gbookF"+F1.guestbook.value+".js" ) // remove file name
   F1.B4.value = " Add to the local disk file: " + s1
 }
 
 
 function submitPl( formName ){
   validate();
   var f = window.document.forms[ "F1" ] 
   F1.action="/cgi-bin/guestbook.pl"
 //  F1.action="http://localhost/cgi-bin/guestbook.pl"
   F1.target="popup"
 
   F1.nextpage.value=location
 
   F1.submit()
   return ( 1==1 )
 }
 
 
 
 function strip ( s ) {
   s= s.replace( /\\/g,"\\\\")
   s= s.replace( /\"/g,"\\\"")
   s= s.replace( /\'/g,"\\\'")
   s= s.replace( /\n/g,"<BR>\\n")
   s= s.replace( /\r/g,"")
   return s
 }
 
 function focusB1( formName ){
   // Function that uses Active X to emulate remote ASP server
   var f = window.document.forms[ "F1" ]
   f.B1.focus()
 }
 
 
 function AXopFile( formName ){
   // Function that uses Active X to emulate remote ASP server
   var f = window.document.forms[ "F1" ] 
   var fs,s;
   var now = new Date()
   var s
   validate();
   s = self.location.pathname	// gives /D:\data\hp\gbbook\DHRsorted.htm
   s = s.replace( /\//g,"") // remove leading /
   s = s.replace( /codeSorted.htm/g,"gbookF"+f.guestbook.value+".js" ) // remove file name
   gbook=s
   alert( "Add to local File:\n"+gbook )
 
   var fs = new ActiveXObject("Scripting.FileSystemObject");
 
   if ( ! fs.FileExists( gbook ) ) {
     fs.CreateTextFile( gbook )
   }
 
   var ts = fs.OpenTextFile( gbook ,8 , 0 );
 
   ts.WriteLine("//Appended using Active X.");
 
   ts.WriteLine( "gbF( ")
   ts.WriteLine( "'" + strip ( f.name.value ) + "',")
   ts.WriteLine( "'" + strip ( f.email.value ) + "',")
   ts.WriteLine( "'" + now + "',")
   ts.WriteLine( "'127.0.0.1'," )
   ts.WriteLine( "'" + strip ( f.userfield1.value ) + "',")
   ts.WriteLine( "'" + strip ( f.userfield2.value ) + "',")
   ts.WriteLine( "'" + strip ( f.userfield3.value ) + "',")
   ts.WriteLine( "'" + strip ( f.userfield4.value ) + "',")
   ts.WriteLine( "'" + strip ( f.comments.value ) + "'" )
   ts.WriteLine( ");" )
   ts.WriteLine( " " )
 
   ts.Close();
 
   location.reload( 1==1 )
 }
 </script>
 </body>
 </html>
 

Collated Page codewebpagecollate.htm

<html>
 <head>
 <link rel="stylesheet" href="../club.css">
 </head>
 <body>
 <script>
 
 var gbA = new Array()
 var count
 
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
   var r = new Object()
 
   	r.name       = name
 	r.email      = email
 	r.postedOn   = postedOn
 	r.IPaddress  = IPaddress
 	r.userfield1 = userfield1
 	r.userfield2 = userfield2
 	r.userfield3 = userfield3
 	r.userfield4 = userfield4
 	r.comments   = comments
 	r.link	     = ""
 
         // add extra fields, as data is loaded
 	r.count      = count
 
 	var index =  userfield1
 
 	// Is this a new item, 
 	//  if so store in gbA, 
 	//else 
 	//  prepend this record onto front of the list.
 
         if ( typeof( gbA[ index ] ) =="undefined" ){
  	  gbA[ index ] = r
         } else {
           // copy old object into r.link 
 	  // This forms a linked list, with oldest at the end.
           r.link = gbA[ index ]
           gbA[ index ] = r
 
 	}
         count=count+1
 	return this
 }
 
 function displayRow( r ){
     self.document.write( 
       "<Tr>"+
         "<TD >"+ r.name+ "</TD><TD >"+ r.email+"</TD><TD >"+ r.postedOn+"</TD>"+
         "<TD >"+ r.IPaddress+ "</TD>"+
       "</TR>"+
       "<TR>"+
         "<TD ><B>"+ r.userfield1+"</B></TD><TD >"+ r.userfield2+	"</TD>"+
         "<TD >"+ r.userfield3+"</TD><TD >"+ r.userfield4+	"</TD>"+
       "</TR>"+
       "<TR>"+
         "<TD></TD><TD colspan = 7 class=code ><PRE>" + r.comments.replace(/<BR>/gi,"\n") + "</PRE></TD>"+
       "</TR>"
     )
 }
 
 
 </script>
 
 
 <h4> The data from the guestbook is Collated by userfield1</h4>
 This page uses an associative array gbA indexed by userfield1 gbA[ userfield1 ]<P>
 
 A linked list is used to prepend the latest instance of gbA[ userfield1 ]. <P>
 The list is sorted before presentation.
 
 <table border=2 >
 
 <script  language = "JavaScript"  src = "/gb/gbookFXXX.js"  type = "text/javascript">  </script>
 
 <script>
 
   var indexA = new Array()
   count=0
   for( I in gbA ){
     indexA[ count ] = I
     count 	    = count + 1	
   }
   indexA.sort()
 
   for( var cnt=0; cnt < indexA.length; cnt++ ){
     var r = gbA[ indexA[cnt] ]
   
     self.document.write( "<TR><TH colspan = 8>&nbsp;<B>"+ r.userfield1+"</B> </TH></TR>" )
     displayRow( r )
     var sr=r
 
     // walk down the linked list 	
     while( sr.link != "" ){
 	r=sr.link
         displayRow( r )
 	sr=sr.link
     }
   }
 </script>
 </table>
 
 </body>
 </html>
 

Collated Page codewebpagetables.htm

<html>
 <head>
 <link rel="stylesheet" href="../club.css">
 </head>
 <body>
 <script>
 
 var gbA = new Array()
 var count = 0
 var indexA = new Array()
 
 function gbF( name,email,postedOn,IPaddress,userfield1,userfield2,userfield3,userfield4,comments){
   var r = new Object()
 
   	r.name       = name
 	r.email      = email
 	r.postedOn   = postedOn
 	r.IPaddress  = IPaddress
 	r.userfield1 = userfield1
 	r.userfield2 = userfield2
 	r.userfield3 = userfield3
 	r.userfield4 = userfield4
 	r.comments   = comments
 	r.link	     = ""
 
     	// add extra fields, as data is loaded
 	var index =  userfield1
 	// Is this a new item, 	if so store in gbA, else prepend this record onto front of the list.
         if ( typeof( gbA[ index ] ) =="undefined" ){
  	  gbA[ index ] = r
 	  // also save index in an array so that it can be sorted ising indexA.sort()
 	  indexA[ indexA.length ] = index
         } else {	
           // copy old object into r.link 
 	  // This forms a linked list, with oldest at the end.
           r.link = gbA[ index ]
           gbA[ index ] = r
 	}
         count=count+1
 	return this
 }
 
 function displayRow( r ){
     self.document.write( 
       "<Tr>"+
         "<TD >"+ r.name+ "</TD><TD >"+ r.email+"</TD><TD >"+ r.postedOn+"</TD>"+
         "<TD >"+ r.IPaddress+ "</TD>"+
       "</TR>"+
       "<TR>"+
         "<TD ><B>"+ r.userfield1+"</B></TD><TD >"+ r.userfield2+	"</TD>"+
         "<TD >"+ r.userfield3+"</TD><TD >"+ r.userfield4+	"</TD>"+
       "</TR>"+
       "<TR>"+
         "<TD></TD><TD colspan = 7 class=code ><PRE>" + r.comments.replace(/<BR>/gi,"\n") + "</PRE></TD>"+
       "</TR>"
     )
 }
 </script>
 
 <h4> The data from the guestbook is Collated by userfield1</h4>
 This page uses an associative array gbA indexed by userfield1 gbA[ userfield1 ]<P>
 A linked list is used to prepend the latest instance of gbA[ userfield1 ]. <P>
 The list is sorted ( alphbetically and case sensitive ) before presentation.<P>
 When displayed a speparate table is used for each value of USERFIELD1. Use the links below to jump to the table.<P>
 
 <table border=2 >
 <!-- local guestbook in same place as guestbook.asp --> 
 
 <script  language = "JavaScript"  src = "/gb/gbookFXXX.js"  type = "text/javascript">  </script>
 
 <script>
   indexA.sort()
   for( var cnt=0; cnt < indexA.length; cnt++ ){
     self.document.write( "<A href=#T" + cnt + ">" + indexA[ cnt ] + "<A> | " )
   }
   for( var cnt=0; cnt < indexA.length; cnt++ ){
     var r = gbA[ indexA[cnt] ]
     self.document.write( "<H4> <A name=T"+cnt+">"+r.userfield1+"<H4>" )
     self.document.write( "<table>" ) 	
     displayRow( r )
     var sr=r
     // walk down the linked list 	
     while( sr.link != "" ){
 	r=sr.link
         displayRow( r )
 	sr=sr.link
     }
     self.document.write( "</table>" ) 	
   }
 </script>
 </body>
 </html>