A CGI script with Emacs Lisp

I fiddled with the code in this post to make a CGI script in Elisp (Emacs Lisp) that would take the date provided on web page’s form and spit out a new page with that date converted to several different calendars.

The HTML for the page with the input form is about as stripped-down as you can get:

 1:  <html>
 2:  <head>
 3:    <title>Convert a Date</title>
 4:  </head>
 5:  <body>
 6:    <h1>Convert a Date</h1>
 7:    <form method="get" action="cgi-bin/date-convert.cgi">
 8:      Month: <input type="text" name="month" value="6"><br />
 9:      Day: <input type="text" name="day" value="6"><br />
10:      Year: <input type="text" name="year" value="1945"><br />
11:      <input type="submit" Value="Convert">
12:    </form>
13:  </body>
14:  </html>

The important things are

  1. to get the correct location of the CGI script in Line 7;
  2. to have the form submitted as a “get;” and
  3. to have the three input elements of the form in month, day, year order.

The CGI script, “date-convert.cgi,” looks like this:

 1:  #!/usr/bin/emacs --script
 3:  (require 'calendar)
 4:  (setq query (getenv "QUERY_STRING"))
 6:  (setq query-list (mapcar (lambda (x) (split-string x "=")) (split-string query "&")))
 7:  (setq my-date (mapcar (lambda (x) (string-to-number (cadr x))) query-list))
 9:  (princ
10:    (concat
11:      "Content-type: text/html\n\n"
12:      "<html>\n"
13:      "<head>\n<title>Converted Date</title>\n</head>"
14:      "<body>\n"
15:      "<h1>Converted Date</h1>\n"
16:      "<table>\n"
17:      "<tr><td align=right>Gregorian:</td><td>" (calendar-date-string         my-date)
18:      "</td></tr>\n"
19:      "<tr><td align=right>ISO:</td><td>"       (calendar-iso-date-string     my-date)
20:      "</td></tr>\n"
21:      "<tr><td align=right>Julian:</td><td>"    (calendar-julian-date-string  my-date)
22:      "</td></tr>\n"
23:      "<tr><td align=right>Hebrew:</td><td>"    (calendar-hebrew-date-string  my-date)
24:      "</td></tr>\n"
25:      "<tr><td align=right>Islamic:</td><td>"   (calendar-islamic-date-string my-date)
26:      "</td></tr>\n"
27:      "<tr><td align=right>Chinese:</td><td>"   (calendar-chinese-date-string my-date)
28:      "</td></tr>\n"
29:      "<tr><td align=right>Mayan:</td><td>"     (calendar-mayan-date-string   my-date)
30:      "</td></tr>\n"
31:      "</table>\n"
32:      "</body>\n"
33:      "</html>\n" ))

Most of this is the same as my previous date conversion script, but with a bunch of HTML tags added. The interesting stuff is in Lines 4-7. Because the form uses the “get” method to submit its data, the script gets called with a URL that looks like


The stuff before the question mark is the address of the CGI script; mine happens to be in the cgi-bin folder in the Sites folder of my home folder on my Mac. Everything after the question mark is put in the environment variable named QUERY_STRING. Line 4 uses the Elisp “getenv” function to get that environment variable.

Line 6 splits the query string, first on the ampersands, then on the equals signs. The result is a list of lists that looks like this

(("month", "6") ("day", "6") ("year", 1945))

Line 7 plucks the second item (the “cadr” in Lisp terms) from each of the nested lists and converts it to a number. The result is a list like this

(6 6 1945)

which is the date form used by the various conversion functions in the Emacs calendar library. The items in the list are in the same order as the input elements in the form, which is why I said it was important to put them in month, day, year order. This list, in the “my-date” variable, is the argument to all the calendar functions in Lines 17-29.

The power of Lines 6 and 7 come from the use of the “mapcar” function, which applies a function to each item in a list, and the “lambda” construct, which allows anonymous functions to be created on the fly. Lispers will recognize that Lines 4-7 could have been reduced to a single statement, eliminating the need for the “query” and “query-list” variables.

The result is a nice little table with the converted dates. As I said above, I have these two files installed in the Apache “user directory” on my Mac and they work just fine. Unfortunately, the older version of Emacs on the leancrew.com web host doesn’t support the shebang line, so I couldn’t make a publicly-available version for anyone to run. If I find a way to work around that, I’ll post an update.