Writing simple CGI applications using vanilla Tcl

Purpose: describe for the gentle reader how one can do simple CGI applications with nothing up our sleeves...

First, here's a sample CGI script:

#! /usr/tcl84/bin/tclsh

proc generate_page {} {
    puts stdout "Content-type: text/html\n"
    puts stdout "<HTML>"
    puts stdout "<HEAD>"
    puts stdout "<TITLE>Wave to the world! knolbak</TITLE>"
    puts stdout "</HEAD>"
    puts stdout "<BODY>"
    puts stdout "Hello, world!" 
    puts stdout "</BODY></HTML>"

if {[catch { generate_page } err]} {
    puts stdout "Content-type: text/plain\n\n$err"

Some key concepts:

  • The first line tells the web server what program to run to execute the script. Change this line to be the location of the tclsh executable on your system.
  • Your program should output a line containing the content type of the page. In the example, it's an HTML file.
  • The Content-type line MUST be followed by a blank line, otherwise you'll get an error message. Note that puts, by default, adds its own newline. The temptation is to specify two, but one is correct.
  • The remainder of the script outputs a web page (in this example).

Now, what do you do with it?

That's a bit tougher to answer. See, it all depends on how the http server was configured. The best thing to do is to contact the person who knows about the specific web server and ask.

"But", you ask, "what if I am the person who installed the server, and I have no idea what the server is expecting?"

Well, all I can do is suggest that you find a newsgroup, mailing list, or web forum that supports the server in question, and ask the kind people there what the default directories, naming conventions, etc. are.

A good place to start is http://www.equi4.com/259 . 20110228 broken link

The "#! /usr/tcl84/bin/tclsh" is more specific to Apache type of servers. Windows IIS 4.x and later does NOT use this, it uses a script map to control execution based on file extension names (e.g., *.cgi, *.cgt, *.tcl)

Yes - This is true - But the "#! /usr/tcl84/bin/tclsh" does not hurt the application - as it is seen as a comment by the Tcl Intrepreter when you execute it via IIS. We use both web environments systems here, the same code runs on Apache and IIS. But I do see you point that is can be dropped. (DB)

You might also want to consider using Don Libes cgi.tcl also - it can be very handy for, HTML Generation, forms and form manipulations. Additionally, I would recommend TimpleSQL if you want to CGI content into a database.

To do anything interesting with CGI's, though, you need to be able to *receive* data as well as send it, and that's where ncgi, cgi.tcl and company come in. CL finds it not-hard to receive CGI data "by hand", wonders why all the "puts stdout ..." above, rather than just "puts ..."

I also wonder why the example uses all those "puts stdout ..." rather than just "puts". And I wonder how I can print the output of a command instead of "Hello, world!". I tried and the output is printed OK, but nothing else afterwards. Say, "</body></html>" don't get printed.

ECS - I am not sure what you mean for the output of a command... I changed the above proc to display local time.

#! /usr/tcl84/bin/tclsh

proc generate_page {} {
    puts stdout "Content-type: text/html\n"
    puts stdout "<HTML>"
    puts stdout "<HEAD>"
    puts stdout "<TITLE>Local time</TITLE>"
    puts stdout "</HEAD>"
    puts stdout "<BODY>"
    puts stdout "Local time is [clock format [clock seconds]]." 
    puts stdout "</BODY></HTML>"

if {[catch { generate_page } err]} {
    puts stdout "Content-type: text/plain\n\n$err"

slebetman: OK, I personally don't feel comfortable removing stuff from the wiki but can we please move this discussion about parray to another page? It really doesn't have anything to do with the subject of this page since the behavior described here does the same "unexpected" thing in plain tclsh/wish. I'm willing to write about it perhaps in a page titled "misunderstandings concerning parray" but that will have to wait until tomorrow.

I run:

puts "a [parray env] b"

I expect...

"a (a long list of vars) b"

...but I get:

"(a long list of vars) a b"


Of course, this works:

puts "a"
parray env
puts "b"

But if I can't intersperse commands with the text and have to use puts for every single line individually, CGI with vanilla Tcl becomes a major pain. Look at the generate_page example. How readable is that?

ECS - parray was a bad choice since it prints on stdout AND returns an empty string :-) try [array get env].

And you can write generate_page as

proc generate_page {} {
    puts stdout "Content-type: text/html

        <TITLE>Local time</TITLE>
        <P>Local time is [clock format [clock seconds]].</P>

Still tough. [array get env] does not produce the same formatted output as [parray env]. In order to reproduce parray's behavior, I would have to foreach on [array get ::env] and everything becomes very very tricky (for sufficiently small values of "tricky" :-) . Assigning variables produces noise and breaks the script (no, assigning variables does not break the script). Wrapping it in a proc that does not return anything produces "empty string" and breaks the script. Wrapping it in a proc that returns something works, but rules out many usual/obvious solutions and require a lot more planning and thinking than it would in a regular shell or Tk/GUI script.

My conclusion, so far, is that using Tcl for CGI is a bad idea. In most languages, especially those better suited to CGI, like Perl and PHP or even Bash, commands do not return or output anything until you "print" or "echo" something explicitly. Since Tcl's commands and [anything in square brackets] always return something, at least an empty string, writing CGI with Tcl becomes such a mine field. Lots of simple lines I have been writing routinely for so long now explode under my feet. Tcl no longer is the nice, laid-back language it used to be.

Of course it is possible, but not the best tool available really. I'd love to hear someone prove me wrong.

You seem to be under the misguided assumption that what you see in an interactive console is what you would see on a web page. Even though every tcl command returns a value, the only thing that will appear on the web page will be what you puts to stdout. Why do you think differently?

As was already pointed out, you picked an unfortunate case to experiment with -- parray isn't a normal tcl command that returns a value; rather it is a utility procedure that does a bunch of puts. You can rewrite it trivially with "foreach" and "array names".

DL Here's a paper that long ago destroyed the myth that Tcl is bad for CGI. http://expect.nist.gov/doc/cgi.pdf . Since this paper was written, quite a bit of software and other approaches have been created to make Tcl-based CGI programming quite pleasant.

joheid Unfortunately this link gives a 404 error. New location of the doc: http://www.nist.gov/customcf/get_pdf.cfm?pub_id=821285

tclbliss 02/28/2011 The reason that it is a myth that Tcl is bad for CGI is that CGI is EASY, CGI is a VERY SIMPLE protocol. CGI is easy in ANY programming language, I could do it in shell, if I wanted to. The problem is that there are lots of "guru" types out there whose only reason for existing is to take something very simple and make it look very complicated. Then they can write and sell 500 page books, explaining something that only needs 10 pages to explain. Or create programming languages like PHP as to make it "simpler" to do "complicated" things. The fact is that it is very easy to do it in Tcl. Tcl is perfect for web page creation, because it is simple, it has good text processing support and you don't have to deal with 3000+ functions that the "gurus" use in PHP. To paraphrase it, you don't need a bulldozer in order to plant a tree. I simple shovel will do. And Tcl is that simple shovel. The only thing that Tcl lacks, unfortunately, is a crowd of developers contributing to various APIs. Developers tend to flock to something that makes money, which is something that decided by some clueless manager in some big corporation. But enough ranting.

Here are the steps to learn CGI:

1. The most difficult one, in my opinion, is setting up the web server, which is not that difficult but most hosting companies out there will not set it up for Tcl. So, you need to have complete control over your server. For a web server, if you have a choice of installing your own, I would recommend Cherokee: http://www.cherokee-project.com/ It is by far the most user friendly open source web server out there. It is also very fast, powerful and easy on resources.

2. Learn HTTP headers: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields While there are lots of them, many are not used in CGI. You only really need to know the response headers for CGI applications.

3. Learn CGI env variables: http://www.cgi101.com/book/ch3/text.html CGI variables are created by a web server, which takes HTTP headers and some other helpful information and converts them into env variables. In Tcl they are stored in "env" array.

4. Learn HTML

5. Learn your programming language so you can manipulate text data, access the database etc. Pretty much ANY language will do for CGI.

What happens during CGI request:

  • A web browser sends a request to the server:
GET /script.tcl HTTP/1.1

Host: localhost

Connection: keep-alive

Accept: */*

User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.98 Safari/534.13

Accept-Encoding: gzip,deflate,sdch

Accept-Language: en-US,en;q=0.8

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
  • A web server will take the headers and convert them into CGI env variables, for example:

Host: localhost becomes HTTP_HOST="localhost"

Which in your Tcl script you can read as "set env(HTTP_HOST)" or "$env(HTTP_HOST)"

The web server also adds more information, like IP address of the visitor: REMOTE_ADDR

  • So, the web server sets the env variables and than executes the script.tcl script.
  • In your script.tcl script, you just read the CGI variables that are useful to your application, like $env(PATH) or $env(QUERY_STRING)
  • And then you respond to the request with your HTML or any other data (text,image,video etc).

Here is the simplest "hello world" CGI application:

append headers "Content-Type: text/plain\n"

set output "hello world"

puts stdout $headers

puts stdout $output

There are helper packages in Tcl library that you can use, although they are absolutely not required for writing CGI applications:

The subst command is extremely useful in generating dynamic web pages. Or creating html templates.

Once you know these basics, designing web applications is all up to your creative imagination. Tcl has a rich library of packages to make it easeir to create very complex web applications.

tclbliss 03/04/2011 -- You can still put PHP to a good use ;) -- let it serve TCL CGI scripts: [L1 ]