CGI Scripting
Perl CGI Index
Basic CGI Scripting
Simple CGI Demo
Styling CGI Output
Sharing Perl Code
CGI Code Suite
CGI Environment
Next section...
Web Applications
Demo Source Code
Example #1 source
Example #2 source
Example #3 source
dwd.pm (#3) source
Example #4 source
dwd.pm (#4) source
dwd.conf source

CGI Scripting

Simple CGI Demo - Example #1

This CGI demonstration program 'Temperature Converter' provides a simple webpage form into which you can enter a temperature and a unit. When you submit the form, the temperature is calculated in all three main units, Centigrade, Fahrenheit and Kelvin and the return page will show these values.

Execute the program...

In many ways this program is a good deal longer than it really needs to be, however the object of the exercise is to demonstrate a variety of CGI and Perl methods, not to write a blindingly clever piece of code in just a few incomprehensible lines. In reality a CGI program would be overkill for such a trivial task, a simple piece of JavaScript in the page could have taken care of it more easily.

Anyway, let's look at the code, view the »program source code, this will open in a separate window, keep it open for reference as you read this or refer to the code extracts below. The line numbers to the left are for reference only and are not part of the CGI code, click on the linked line numbers to go to the relevant notes for that section of code.

001 #!perl -w
002 #
003 #  cgi_demo.pl    (c) Andy Belcher DandyWebDesign 2006
004 #
005 #  A simple web-page widget to convert temperatures and demonstrate simple
006 #  CGI forms. Takes a single input temperature and a unit (F, C or K) and
007 #  displays the equivalent in all three units.
008 #  NOTE!  This is a CGI demonstration example intended to show
009 #  different methods rather than an efficient piece of coding!
010 #
011 ######################################################################
012
013 #  Pull in required Perl modules
014 use strict;
015 use CGI qw(:all);
016 use CGI::Carp qw(fatalsToBrowser);

Line 1 is the so called 'shebang' line. When shell scripts are executed on a unix system this line defines which application should be used to parse the script. Obviously this needs to be Perl and so this line contains the path to the perl executable program which will run the script. The exact form of this line varies as it is system specific, common values are: /bin/perl - /usr/local/bin/perl - /usr/bin/perl Your host service provider will tell you which path is required for their site, you will obviously need to set your scripts accordingly.
When running perl on a win32 platform this line is different, on some implementations it is not required at all, however on my system (ActiveState Perl win32 on w98) the required line is #!perl with the perl executable directory: c:\perl\bin in my PATH variable.

The -w flag enables taint checking, a security feature which prevents 'tainted' data being passed to the program form anywhere other than the allowed route, ie the legitimate program arguments.

Lines 2-11 are the title and credit block, not strictly necessary, they are nevertheless good coding practise and should always be included in any program you write.

Line 14 Strict is not a module but a pragma or directive. This enforces strict variable scope and declaration discipline. If you turn it off you will find that it is much easier to call variables whenever required without having to declare them but in a large program the results will soon get messy. Leave it on and learn to declare and scope your variables correctly, a good habit to get into in any programing language, not just Perl.

Line 15 CGI is a Perl package which is designed for CGI programs and has many useful functions within it which become available to the program once this module has been loaded. The directive (all) loads all of the available subroutines into memory.

Line 16 This is the Carp package of the main CGI module, in this case we are only loading one particular function: 'fatalsToBrowser'. This routine outputs an error message to the browser screen rather than just getting an error message in the browser and an entry in the web-server error_log. This is very useful during program developement but should really be removed once the program goes into live use.

018 #  DECLARE VARIABLES
019 my $returnurl  = "/tech/cgi/cgi_simpledemo.shtml#notes";  #  LINK - RETURN PAGE
020 my $intemp;
021 my $inunit;

Line 18-21 This is where we declare the variables, because we are using the 'strict' pragma we must declare these variables properly with the 'my' prefix. In this case these scalar variables are declared in the main program body and are therefore global variables, and important point as we will be using a subroutine later on which must also be able to read these scalars. The first value; '$returnurl' defines the URL for the final display which will take us back to this documentation page. The remaining two scalars, '$intemp' and '$inunit' will hold the two input values for the temperature and the unit respectively.

023 #  POPULATE OUTPUT SCALARS WITH DEFAULT VALUES FOR DISPLAY
024 my $outtempc  = 0;
025 my $outtempk  = 273.15;
026 my $outtempf  = 32;

Line 23-26 This defines some more scalars, these will hold the output values, one for each of the three temperature units we are dealing with here, note the last letter of each variable name!
Note also that we set an explicit default value for each one so that when the calculation runs it will have defined values to work with, generally sums with null values result in errors and warnings and are almost never correct!

028 #  READ PARAMETERS, FOR EACH PARAMETER USE THE 'param' FUNCTION (PART
029 #  OF THE CGI MODULE) TO EXTRACT THE ARGUMENTS FROM THE FORM.
030 #  NOTE THAT IF THE ARGUMENT IS NOT PRESENT $ARG{PARA} IS UNDEFINED
031 #  NOTE ALSO THAT THE ARGUMENT NAMES MATCH THE FIELD NAMES IN THE FORM
032 $intemp = param('intemp');
033 unless((defined($intemp)) && ($intemp)) {$intemp = 0;}
034 $inunit = param('inunit');
035 unless((defined($inunit)) && ($inunit)) {$inunit = "C";}

Line 32 This reads the value of the input parameter 'intemp' into the scalar variable '$intemp' using the function 'param'. This function is part of the CGI Perl module and the main reason why we use it. It automatically takes care of any syntax issues or complications and returns the value simply and cleanly from the function. Very handy!

Line 33 It is not always possible to guarantee that the required argument will be present in the input. If this is the case the 'param' function will return a value of 'UNDEFINED', essentially NULL. This can cause problems later in the program so this line tests to see if '$intemp' is undefined and if it is forces an explicit value onto it, in this case a default to 0.
Note also the combined test (&& is the AND logical operator) This will only evaluate the second condition if the first is not met, one reason why Perl is so fast, it wastes no time! As soon as a condition is met which invalidates the overall condition logical processing stops and an overall false is returned. Here we are dealing with an 'unless', effectively 'if not' and so the case is reversed but the principle is the same.

Lines 34-35 are essentially the same as 31-32, they perform the same function for the input unit, if undefined or missing then it is set as 'C'.

037 #  PERFORM REQUIRED CALCULATION
038 if($inunit eq "F")
039   {$outtempf  = $intemp;
040   $outtempc   = &conv_temp($intemp,"f2c");
041   $outtempk   = &conv_temp($outtempc,"c2k");
042   }
043 elsif($inunit eq "K")
044   {$outtempk  = $intemp;
045   $outtempc   = &conv_temp($intemp,"k2c");
046   $outtempf   = &conv_temp($outtempc,"c2f");
047   }
048 else
049   {$outtempc  = $intemp;
050   $outtempk   = &conv_temp($intemp,"c2k");
051   $outtempf   = &conv_temp($intemp,"c2f");
052   }

Lines 37-52 Deal with the actual calculation. Because we set default values at the start the calculation will still proceed without undefined value issues even on the first call to this program when no input values will have been set yet.

The calculation itself has been taken away as a sub-routine; '&conv_temp(t,c)' where 't' is the input temperature and 'c' is a code which tells the function the required input and output units.
The overall calculation is controlled by an ELSIF (note spelling!) logic block which determines which units are being supplied, and which are required, and make the appropriate function calls to populate all of the output values.
Note also that the same value is used as the assumed default, in this case 'C' as is the default input value in the form. The code would still work this was not done but it is generally good practise to do this, in the event of odd inputs this can lead to confusion otherwise!

054 #  OUTPUT PAGE
055 print header;
056   #  NOTE! This MUST be the very first output else the program will fail.
057   #  'header' is a function from the CGI module which adds the required
058   #  HTTP headers to the output stream, if this does not get output first
059   #  an error will be returned "Malformed header from program..."
060   #
061 print<<HERE;

Now that everything is worked-out we are ready to output the return page with the form and the calculated values, (or if this is the first call to the program then the form plus the calculated default values)

Line 55 This is the most critical line of any CGI program, the return header. This is a code that prefixes the output stream from the program and identifies the text stream for what it is. This MUST be the very first thing that is output from the program, if it isn't an error will be returned along the lines of 'Malformed header from script...'.
Here we are using another of the CGI package functions: 'header'. This automatically outputs the required string.

It is very tempting to ensure that this is the first output simply by placing this line at the very start of the program, however this can cause problems with larger or more complex programs which take a perceptible amount of time to execute, especially if they have to access a database or read/write data from/to a file. In this case the browser will receive the return from the server and clear the screen and perhaps even add the top part of the page but then it will hang until the program has finished its job and has started to output the return stream. The appearance at the browser is a page that starts, then hangs for a while and then finally arrives. Looks horrible, don't do it! Do not output the header until all possible processing has been completed, from this point on you should ideally not be doing anything more complicated than ordering lists or making simple logical choice as to what to output in the page so as to give a quick (and apparently seamless) output stream.

From Line 61 onwards we start outputting the page itself, however I shall leave that for now, returning to it later, and instead skip ahead to the bottom of the output section.

087 </body>
088 </html>
089 HERE
090 exit;
091 #  END PROGRAM

Lines87-88 complete the HTML page.

Line89 contains the terminator for the block print statement earlier in the code. (Line 60: print<<HERE;) The string need not be 'HERE' it can be any string such as 'QWERYUIOP' as long as it is matched by an identical string further on.
Note! Make sure there is no trailing whitespace AFTER the HERE end tag else it will not match. This is a great first-time 'gotcha' error. Be warned!

Line 90 terminates the program. Not strictly necessary as there are no more code lines but it is a clean end to the program and good practise.

The remainder of the script is given over to any subroutines, in this case the one that was used to do the actual calculations: 'conv_temp'.

094 sub conv_temp
095   {my ($in,$un) = @_;
096   unless(defined($in)) {$in = 0;}
097   if($un eq "f2c")
098     {unless($in) {$in = 32;}
099     return ($in - 32) * (5/9);
100     }
101   elsif($un eq "k2c")
102     {unless($in) {$in = 273.15;}
103     return $in - 273.15;
104     }
105   elsif($un eq "c2f")
106     {unless($in) {$in = 0;}
107     return ($in * (9/5)) + 32;
108     }
109   elsif($un eq "c2k")
110     {unless($in) {$in = 0;}
111     return $in + 273.15;
112     }
113   else {return $in;}
114   }

Lines 94-114 contain the 'conv_temp' sub-routine. Line 99 is the only definition line required for this sub.

Line 95 is where the arguments are extracted for the function. There is an environment array called @_ which contains the arguments for the function. These may also be accessed individually as $_[0], $_[1],..
The first argument is the temperature value for conversion. The second argument is a short string which determines which units are used for the calculation.

Line 96 ensures that we at set a 0 value if $in is still somehow UNDEFINED.
(This also 'pre-suggests' to the Perl interpreter that this scalar variable is numeric.)

And the rest of the calculation is pretty obvious and fairly standard in any language.
All that is left now is the output section. Basically this is all one block of HTML in a fairly self-explanatory print statement.

061 print<<HERE;
062 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
063   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
064 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
065 <head><title>Demo CGI Form Utility</title>
066 </head>
067 <body>
068 <h1>Perl CGI Demo Program : Temperature Converter</h1>
069 <p>Enter a temperature into the form below and when you submit it the
070 temperature will be displayed in all three units.</p>

Lines 62-64 These output the DOCTYPE definition for the page. In this case this is the xHTML 1.0 transitional doctype, yours might not be however you should output a doctype line of the appropriate HTML standard that you are using in order that your program matches the required W3C standards.

071 <form method="post" action="$ENV{'SCRIPT_NAME'}">
072 <input type="text" name="intemp" size="5" value="" /> Enter a temperature...
073 <br /><select name="inunit">
074   <option value="C" selected="selected">Celsius</option>
075   <option value="F">Fahrenheit</option><option value="K">Kelvins</option>
076   </select> Specify the unit of temperature...
077 <br /><input type="submit" value="Calculate temperatures..." /></form>

Lines 71-77 handle the input form within the page.
In line 71 we have two important features in the form declaration. The first is the METHOD. This can take two values; 'GET' or 'POST'. 'Get' uses the URL to pass additional arguments, 'post' doesn't, extra values must be passed as hidden fields within the form. 'Post' is generally the better method for CGI programing.

The second important directive is 'ACTION' this defines the resource that will be used to receive and process the form input, in this case we add the URL for this program.

Note! instead of hard-coding the program name we can use a Perl environment variable %ENV to read the value of 'SCRIPT_NAME' which in this case holds the value: '/cgi-bin/cgi_demo.pl'.

Line 72 is the HTML for the temperature input field. Note that its name is 'intemp', and that this string matches the value referenced in the param function used to extract the value into the program code.

Line 73 This is the second field, a different type of HTML input field, the pull-down 'SELECT'. Again note that the name matches the argument in the param function call within the program.

078 <table>
079 <tr><th>Centigrade</th><th>Fahrenheit</th><th>Kelvin</th></tr>
080 <tr><td>$outtempc</td><td>$outtempf</td><td>$outtempk</td></tr>
081 </table>

Lines 78-81 contain the HTML for a simple table containing the equivalent temperatures in each of the three units. Nothing ground-breaking here, however note how the variable names are used within line 80.

082 <hr />
083 <p>Ok, that's the program demo, now click here to return to the <a
084   href="$returnurl"
085   title="Return to the documentation for this demo..."
086   >documentation page</a> for this demo program...</p>

Lines 82-86 are not strictly part of the program but instead add in the return link relevant to this demo program, note the use of '$returnurl' to pass the value set into the starting lines of the program. It is always good practise to set values like this as variables declared at the start of the program, never hard-code values like that into the output HTML if you can possibly avoid it as it is very bad practise.

And that is about it, the rest of the code has already been discussed.
I hope that you find this a useful demonstration of a simple CGI program, or at least that it gives you ideas or the incentive to write your own.
If you have any problems or want to know more then .
Andy

Show Style-Switcher...