Using TCL and cgi.tcl to Create and Maintain your Web Page

From EDM2
Jump to: navigation, search

By Eddy Kim

(Note: Here is a link to the files used in this article. Ed.)

Introduction: How can a scripting language help with static web pages?

This article will cover some basic TCL syntax, some cgi.tcl usage and finally some examples and hints on how to get the most from these tools.

TCL (pronounced 'tickle' or T-C-L) is a scripting language available on a wide variety of platforms. Written by John Ousterhout, then maintained by Sun Microsystems, and currently supported by Scriptics (www.scriptics.com), TCL is freely available with source.

The latest OS/2 version of TCL is 7.6, ported by Illya Vaes, and is available on hobbes under /pub/os2/dev/tcl. The latest one I'm aware of is tk42os2x.zip. This package includes a textmode interpreter and a PM version which includes the graphical toolkit, TK.

In addition to the base language, we'll also need cgi.tcl from http://expect.nist.gov/cgi.tcl/ for this article. cgi.tcl was written by Don Libes, also the author of 'expect', the invaluable tool for automating interactive tasks (alas, this is only available for UNIX and NT at the moment).

A good description of cgi.tcl is from the homepage:

cgi.tcl is the CGI support library for Tcl programmers. cgi.tcl can also be used for generating static HTML (such as this page). This enables you to get programming features into HTML, such as variables, if/then/else, file I/O, etc.

Installation: Pre-requisites

Follow the directions in the README file of the TCL package. I've also renamed the text-mode program from tclsh76.exe to tclsh.exe to make things follow my UNIX experience more closely.

After installing the .exe and .dlls and also setting the TCL_LIBRARY variable to the correct location, to test that everything is working:

[OS/2] tclsh76
%

If you see this:

application-specific initialization failed: Can't find a usable init.tcl in the
following directories:
    {} {} ./lib/tcl7.6 I:/tcl7.6/library I:/library

This probably means that Tcl wasn't installed properly.

The interpreter can't find the init.tcl library. Set the TCL_LIBRARY variable so that the interpreter can find its tcl libraries (not the .dlls).

Installing cgi.tcl

cgi.tcl was intended for use in the unix environment, but it works just fine under the OS/2 tcl environment. It is available from the cgi.tcl page as a tar.Z or a tar.gz format which means you'll need 'tar' from hobbes as well as uncompress or gunzip. After decompressing and un-tarring the distribution, you may want to install the cgi package as a tcl package. To do so:

  1. execute the tcl script pkgcreate which will create the file pkgIndex.tcl
  [OS/2] tclsh pkgcreate

  1. Copy the two files, cgi.tcl and pkgIndex.tcl, to a location where tcl will look for libraries. On my installation, I installed the tcl executables in /bin and copied the tcl library to /lib/tcl. Set the variable TCL_LIBRARY=h:/lib/tcl/tcl7.6. Then I created a lib subdirectory under /lib/tcl/tcl7.6/ and copied the two files to lib.
  2. Test that the package is available by running the tcl interpreter and loading the package.
  [OS/2] tclsh
  % package require cgi
  0.7.5
  % p "test"

test

You may also want to copy the doc/ref.txt file which lists and documents all the cgi procedures available.

Quick overview of TCL

TCL is a wonderfully simple language which has a few basic rules and consistent constructs, although if this is your first exposure to the language, it may look quite odd.

  1. TCL is a string based language. Bourne shell programmers will feel right at home, being able to execute the contents of a variable if it is a valid command. Everything is a string, even numbers; there are no variable types or declarations.

Some examples of setting and using variables:

  set command "printf"
  set Name "John Doe"
  set Age 50
  set user(age) $Age
  set user(name) $Name
  set elem id
  set user($elem) 501

  puts "This is the equivalent of $command in TCL"
  puts "Username = $user(name) User age = $user(age) Userid = $user(id)"
  1. Functions in TCL are called procedures and are declared thus:
  proc ProcedureName {args} {
    puts "$args"
  }

Notice the first set of brackets. These are required; if the procedure does not take any arguments, use empty brackets. Also important is the placement of the second left bracket. It must be on the first line, because it allows the interpreter to continue to the next line to finish reading the procedure. To call a procedure, simply use the procedure name with any required arguments.

  Procedure item1 "item 2" {item 3}

Note that the first argument is item1, the second argument is surrounded by double quotes to keep the interpreter from separating the item and 2. The third argument is surrounded by brackets for the same reason as the second argument, but any variable substitution will not be done in brackets as they would in quotes. Don't surround all the arguments in brackets unless you want the procedure to see them as one argument. Procedures can be called within a construct by using square brackets:

  proc1 arg1 [proc2 arg1 arg2] arg3

More detailed coverage of TCL can be found throughout the web. References are at the end of this article.

Using cgi.tcl

Now that we have tcl working and cgi.tcl installed, we can write a simple tcl script that generates an HTML file.

UNIX scripts have the ability to run the interpreter by having "#!/path/to/binary" as the first line of the script. Unfortunately, we don't have that ability. As a workaround, we either execute our scripts with "tclsh scriptname.tcl" or from inside a script "source scriptname.tcl". We cannot use the exec command of TCL because it relies on the UNIX feature previously mentioned.

Running the first example, we have a few tags generated:

[OS/2] tclsh examp1.tcl
<title>title</title>
<body>

This article will cover some basic TCL syntax, some cgi.tcl usage and finally some examples and hints on how to get the most from these tools.

[...]
</body>

At least on my system, after tclsh exits normally, the queue to the command window seems to get locked. i.e. keystrokes to the window are not registered. If I click on the title bar of the window, control returns.

Writing directly to a file

The default behavior of cgi.tcl is to output to stdout, because of the way cgi works. When a web server executes a cgi program, it reads from stdout of the child process and redirects it to the client browser. Since we're not interested in a real cgi program, we can redirect standard output into a file, examp1.html, to capture the static HTML. Or with a little extra work, write a procedure that will change the basic behavior of the cgi.tcl routines so that they write directly to a file.

So let's do that. Running example 2 shows how it's done:

  [OS/2] tclsh examp2.tcl

  [OS/2]

Executing examp2.tcl will not generate any output to the screen (unless there are any errors) and will write all the output into examp2.tcl without any manual redirection of output at the command shell level.

Let's take a close look at what needs to be done to implement this.

After the "package requires cgi" line, we load a tcl file htmllib.tcl. This contains a replacement function for puts which is used by the rest of the cgi.tcl library. In order to replace it, we use the rename procedure to 'move' the old proc out of the way and place ours in its place.

Another procedure in this 'library' is the open_HTML_output procedure. This takes a single argument which opens that file for output and returns the file descriptor of that file. Generally we'll simply give the name of the tcl source file as the argument which can be found in the argv0 variable. We can open different files simultaneously, if we are using frames.

Since the main focus of this article is not tcl, but how to use the higher level libraries, I won't go into the details which are documented in the comments of the library.

cgi.tcl routines

Here's a short overview of the routines available to use that are of interest in creating static HTML.

The following procedures take a single argument.

p string - wraps

...

tags around the string
blockquote string - blockquote tags
h1, h2, ..., h7 - paragraph level tags

br - break tag

These format text:

bold
italic
underline
typewriter
emphasis
string
big
small

Generally they are used within another text procedure:

p "[bold "This is bold"], but this is [emphasis emphasis]"

There are procedures that generate tags that are a pain when writing HTML by hand. These make it much easier.

lt - less than <
gt - greater than >
amp - ampersand &
quote - double quote "
tm - trademark
copyright - copyright

There are procedures for tables, lists, URL handling, and forms which are all described in the ref.txt supplied with cgi.tcl.

Helper functions

Now let's go over some of the sample 'helper' functions I've used in the past.

proc std_footer
This is used to generate a series of tags which also include the current date and time the tcl file was executed.
proc std_margin
This uses tables to generate margins for the left and right side of the page. It is used like this:
  std_margin { #remember the location of the bracket is important!
    p "other statements within"
    std_footer
  }
proc TopicHeader
I use this to generate a colored cell with an anchor. The first argument is the background color and the second are the tags.
  std_margin {
    TopicHeader "#FFFF99" "[bold "This is my topic"]"
  }

proc NOTE
Notes are small grey colored right-aligned sidebars.

Example 3 shows this article as a tcl script which shows the usage of these helper functions.

Possible enhancements

This system works reasonably well for me, as I'm not an expert in HTML nor am I terribly interested in becoming one. It allows just as much power and flexibility as writing the tags by hand, yet also allows the convenience of variables, procedures, and centrally located definitions.

However, one item not covered in this article is the use of multiple files and how to manage their dependencies if they include outside files. If there is interest, I can continue the article with some possibilities.

The helper functions presented here are probably quite simplistic and could be done much better in terms of HTML style. If you have any suggestions, comments or improvements, please e-mail me at ehkim@ibm.net.

References