A simple CGI example in Scheme

This example is tested with Guile 1.8.8, a nice lightweight open-source Scheme. The string-split function is not part of the RSR5 standard, but shouldn’t be too hard to hand-code. I found a ready-made one, but apparently that one requires a single character split expression to be “listed,” which is to say, in the code below, #\= would be replaced by '(#\=). As is always the case setting up new languages for CGI access, there is a need to tweak /etc/apache2/httpd.conf. This is what I’m running with:

LoadModule cgi_module modules/mod_cgi.so
ScriptAlias /cgi-bin/ /var/www/perl/
AddHandler cgi-script .cgi .pl .scm

<Directory "/var/www/perl/">
Options +ExecCGI
</Directory>

<Directory "/var/www/scheme/">
Options +ExecCGI
</Directory>

The Scheme script is pretty trivial. Its task is to display the environment variables and their values as a table. Here is the script:

#!/usr/bin/guile -s
!#


; Convert an element of the return value of (environ), which is a single string
; in which the #\= character separates key and value, to a pair in which the
; key is the car (suitable for lookup via assoc) and the value is the cdr.  If
; the string contains more than one #\=, only the first one is a delimiter;
; hence re-joining the cdr of the result of string-split.
(define (assocify equate-string)
 (let ((stuff (string-split equate-string #\=)))
  (cons (car stuff) (string-join (cdr stuff) "="))))



; Return the value of the environment variable QUERY_STRING
(define (query-string)
 (cdr (assoc "QUERY_STRING" (map assocify (environ)))))



; Escape out angle brackets in text.  This of course needs to be modified to
; cover all escapable characters.
(define (deangulate text)
 (string-join (string-split (string-join (string-split text #\<) "&lt;") #\>) "&gt;"))



; Output one table row, containing an environment variable in the first column
; and its value in the right column.
(define (display-environment-row key-value-pair)
 (let ((key (car key-value-pair)) (value (cdr key-value-pair)))
  (display "<tr><th>")
  (display key)
  (display "</th><td>")
  (display (deangulate value))
  (display "</td></tr>\n")))



; Output a table containing all of the environment variables and their values.
(define (display-environment-table title)
 (display "<table>\n<caption>")
 (display title)
 (display "</caption>\n<thead>\n<tr><th>varable</th><th>value</th></tr>\n</thead>\n")
 (display "<tbody>\n")
 (let ((environment-assoc-list (map assocify (environ))))
  (for-each display-environment-row environment-assoc-list))
 (display "</tbody>\n")
 (display "</table>\n"))
  


; Output a <head> element for our web page.
(define (display-head title)
 (display "<head>\n")
 (display "<meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />\n")
 (display "<link rel=\"stylesheet\" href=\"/scheme/cgi-test.css\" type=\"text/css\" />\n")
 (display "<title>")
 (display title)
 (display "</title>\n")
 (display "</head>\n"))



; Output the <body> element for our web page, which contains a table listing
; the environment variables and their values.
(define (display-body title)
 (display "<body>\n")
 (display-environment-table title)
 (display "</body>\n"))



; Output a complete web page.
(define (display-document title)
 (display "Content-type: text/html\n\n")
 (display "<!DOCTYPE HTML>\n")
 (display-head title)
 (display-body title)
 (display "</html>\n"))



; The function call that does it all:
(display-document "Hello, world!")

Below is a screenshot. Notice the slash after the filename (cgi-test.scm), and how this is reflected in the value of REQUEST_URI. Also note the GET parameters, reflected in both REQUEST_URI and QUERY_STRING.

Leave a comment

Your email address will not be published. Required fields are marked *