The new CGI module
It's been some time now since I made my first CGI module. For the time being, it sufficed, but after some time of not using it, it was kind of hard to get using it again. There were some design flaws. So it was time to start a new CGI module and now with better and more functions. The goal is to create a simple yet efficient and ultrafast webshop, based on the functions inside the new CGI module.
The old modulename was lower case only: cgi.def and cgi.mod. This new version is called CGI: CGI.def and CGI.mod do all the work.
CGI.def
At the moment, I am working on version 0.4; the previous versions were rather small and too similar to cgi.def and hence were not enough reason to publish about it.
DEFINITION MODULE CGI; (* CopyLeft 2009 jan@verhoeven272.nl This CGI library supersedes the former library 'cgi'. ver does date ----- ---------------------------------------------- ----------- 0.1 SetServer & EnvVar 08 Jun 2009 0.2 Added TYPEs for SetServer 10 Jun 2009 0.3 Add variable table as a linked list 11 Jun 2009 0.4 Skip 'Identifier' in favor of 'Strings.String' Added 'ShowList' procedure for debugging Renamed 'SetServer' into 'SetMimeType' 13 Jun 2009 *) IMPORT Strings; TYPE MimeType = (text, html, gif, jpeg, ps, mpeg); ReqType = (get, post); VarPtr = POINTER TO VarNode; VarNode = RECORD varName, value : Strings.String; next : VarPtr END; VAR thisVar : VarPtr; PROCEDURE StoreVar (CGIvar : Strings.String) : BOOLEAN; PROCEDURE FindVar (str : Strings.String) : BOOLEAN; PROCEDURE ShowList; PROCEDURE ConvertHex (str : Strings.String) : CHAR; PROCEDURE SetMimeType (mime : MimeType); PROCEDURE EnvVar (name : Strings.String; VAR value : Strings.String) : BOOLEAN; END CGI.
| MimeType | This is slightly different from the previous version. Same types only now fully written in lower case. |
| ReqType | This is new. It keeps track of the method used in the FORM data: GET or POST. Not used at the moment. Perhaps later. |
| VarNode |
I created a linked list to store all the data which is used or produced by the FORM and the CGI executable.
This way I don't need to set up lots and lots of static variables; one linked list will do.
The linked list is automatically sorting. New entries are inserted such that the list is in alphabetical order all the times. 'VarPtr' and 'thisVar' are pointers that relate to this kind of 'symbol table'. |
| StoreVar | StoreVar traverses the linked list and tries to find an empty slot where to insert the new variable. If the variable already exists, the procedure returns FALSE. Otherwise the CGI variable 'thisVar' is filled with the address of the new slot. |
| FindVar | With this function, you traverse the linked list, looking for a particular name. If the last element is reached, FALSE is returned. If there is a match, then 'thisVar' is loaded with the address of the corresponding memory slot. |
| ShowList | Lists the name/value pairs that occupy the linked list; for debugging purposes. |
| ConvertHex | Handles the conversion from '%xx' values back into readable characters. |
| SetMimeType | Sets the MIME type for the upcoming page or output |
| EnvVar | Tries to find a CGI environment variable with the specifid name and if found, puts it in the value specified in the function call. In all other cases, FALSE is returned and the value contains nonsense data. |
CGI.mod
And here is the implementation module of CGI. The code is simple and straightforward, as Modula-2 (and any other language) should be.
IMPLEMENTATION MODULE CGI;
(* CopyLeft jan@verhoeven272.nl
For version information, consult the DEF file. *)
IMPORT Arguments, InOut, MemPools, NumConv, Strings, SYSTEM;
(*
TYPE VarPtr = POINTER TO VarNode;
NameNode = RECORD
varName,
value : Strings.String;
next : VarPtr
END;
*)
VAR envTable : Arguments.ArgTable;
VarPool : MemPools.MemPool;
(* thisVar, *)
firstVar : VarPtr;
PROCEDURE StoreVar (CGIvar : Strings.String) : BOOLEAN;
VAR this, prev, new : VarPtr;
result : INTEGER;
BEGIN
this := firstVar;
prev := NIL;
LOOP
result := Strings.compare (this^.varName, CGIvar);
IF result = 0 THEN RETURN FALSE END;
IF result = 1 THEN EXIT END;
prev := this;
this := this^.next
END;
MemPools.PoolAllocate (VarPool, new, SYSTEM.TSIZE (VarNode));
new^.varName := CGIvar;
new^.next := this;
IF prev = NIL THEN
firstVar := new
ELSE
prev^.next := new
END;
thisVar := new;
RETURN TRUE
END StoreVar;
PROCEDURE FindVar (str : Strings.String) : BOOLEAN;
VAR thisOne, nextOne : VarPtr;
BEGIN
thisOne := firstVar;
LOOP
IF Strings.StrEq (thisOne^.varName, str) THEN
thisVar := thisOne;
RETURN TRUE
END;
IF thisOne^.next = NIL THEN EXIT END;
thisOne := thisOne^.next
END;
RETURN FALSE
END FindVar;
PROCEDURE ShowList;
VAR thisOne : VarPtr;
str : Strings.String;
BEGIN
thisOne := firstVar;
REPEAT
str := thisOne^.varName;
InOut.WriteString (str);
InOut.WriteString (" = ");
str := thisOne^.value;
InOut.WriteString (str);
InOut.WriteLn;
thisOne := thisOne^.next
UNTIL thisOne^.next = NIL;
InOut.WriteBf
END ShowList;
PROCEDURE SetMimeType (mime : MimeType);
VAR type : Strings.String;
BEGIN
CASE mime OF
html : type := 'text/html' |
gif : type := 'image/gif' |
jpeg : type := 'image/jpeg' |
mpeg : type := 'video/mpeg' |
ps : type := 'application/postscript'
ELSE
type := 'text/plain'
END;
InOut.WriteString ('Content-type:');
InOut.WriteString (type);
InOut.WriteLn;
InOut.WriteLn
END SetMimeType;
PROCEDURE EnvVar (name : Strings.String; VAR value : Strings.String) : BOOLEAN;
VAR found : BOOLEAN;
chr : CHAR;
i, j : CARDINAL;
eNvar : Strings.String;
BEGIN
found := FALSE;
i := 0;
LOOP
IF envTable^ [i] = NIL THEN RETURN FALSE END;
Strings.Assign (eNvar, envTable^ [i]^);
IF Strings.pos (name, eNvar) = 0 THEN
found := TRUE;
EXIT
END;
INC (i)
END;
i := 0; j := 0;
REPEAT
chr := eNvar [i];
INC (i)
UNTIL chr = '=';
REPEAT
chr := eNvar [i];
value [j] := chr;
INC (i);
INC (j);
UNTIL (chr = 0C) OR (i > HIGH (eNvar));
value [j] := 0C;
RETURN TRUE
END EnvVar;
PROCEDURE ConvertHex (str : Strings.String) : CHAR;
VAR num : CARDINAL;
ok : BOOLEAN;
BEGIN
NumConv.Str2Num (num, 16, str, ok);
IF NOT ok THEN
InOut.WriteString ("Error in number : ");
InOut.WriteString (str);
InOut.WriteLn;
HALT
END;
RETURN CHR (num)
END ConvertHex;
BEGIN
Arguments.GetEnv (envTable);
MemPools.NewPool (VarPool);
MemPools.PoolAllocate (VarPool, firstVar, SYSTEM.TSIZE (VarNode));
firstVar^.varName := "|||";
firstVar^.next := NIL
END CGI.
Some points to keep in mind:
Page created on 13 June 2009 and
Page equipped with FroogleBuster technology