home *** CD-ROM | disk | FTP | other *** search
/ Practical Programming in Tcl & Tk (4th Edition) / TCLBOOK4.BIN / pc / exsource / cgilib.tcl < prev    next >
Text File  |  2003-04-15  |  5KB  |  219 lines

  1. # cgilib.tcl
  2.  
  3. # This is the cgilib.tcl that I actually use for CGI scripts,
  4. # although I end up using a fair amount more code than this,
  5. # especially inside TclHttpd
  6.  
  7. # Note that Cgi_Parse has some funky semantics where if there is
  8. # only one instance of a form value it doesn't add list structure.
  9. # You have to know what to expect to use this properly.  If you
  10. # need to be fully general, I suggest you use
  11. # foreach {name value} [Cgi_List] { ... }
  12. # instead of Cgi_Par
  13.  
  14. proc Cgi_Parse {} {
  15.     global cgi query
  16.     set query [Cgi_Query]
  17.     regsub -all {\+} $query { } query
  18.     foreach {name value} [split $query &=] {
  19.     set name [CgiDecode $name]
  20.     if [info exists list($name)] {
  21.         set cgi($name) [list $cgi($name) [CgiDecode $value]]
  22.         unset list($name)
  23.     } elseif [info exists cgi($name)] {
  24.         lappend cgi($name) [CgiDecode $value]
  25.     } else {
  26.         set cgi($name) [CgiDecode $value]
  27.         set list($name) 1    ;# Need to listify if more values are added
  28.     }
  29.     }
  30.     return [array names cgi]
  31. proc Cgi_List {} {
  32.     set query [Cgi_Query]
  33.     regsub -all {\+} $query { } query
  34.     set result {}
  35.     foreach {x} [split $query &=] {
  36.     lappend result [CgiDecode $x]
  37.     }
  38.     return $result
  39. }
  40. proc Cgi_Query {} {
  41.     global env
  42.     if {![info exists env(QUERY_STRING)] ||
  43.         [string length $env(QUERY_STRING)] == 0} {
  44.     if {[info exists env(CONTENT_LENGTH)] &&
  45.         [string length $env(CONTENT_LENGTH)] != 0} {
  46.         set query [read stdin $env(CONTENT_LENGTH)]
  47.     } else {
  48.         fconfigure stdin -blocking 0
  49.         if {[gets stdin query] < 0} {
  50.         set query ""
  51.         }
  52.     }
  53.     } else {
  54.     set query $env(QUERY_STRING)
  55.     }
  56.     set env(ALT_QUERY_STRING) $query
  57.     return $query
  58. }
  59. proc CgiDecode {str} {
  60.     # Protect Tcl special chars
  61.     regsub -all {[][\\\$]} $str {\\&} str
  62.     # Replace %xx sequences with a format command
  63.     regsub -all {%([0-9a-fA-F][0-9a-fA-F])} $str {[format %c 0x\1]} str
  64.     # Replace the format commands with their result
  65.     return [subst $str]
  66. }
  67. # do x-www-urlencoded character mapping
  68. # The spec says: "non-alphanumeric characters are replaced by '%HH'"
  69.  
  70. for {set i 1} {$i <= 256} {incr i} {
  71.     set c [format %c $i]
  72.     if {![string match \[a-zA-Z0-9\] $c]} {
  73.         set UrlEncodeMap($c) %[format %.2x $i]
  74.     }
  75. }
  76.  
  77. # These are handled specially
  78. array set UrlEncodeMap {
  79.     " " +   \n %0d%0a
  80. }
  81.  
  82. # 1 leave alphanumerics characters alone
  83. # 2 Convert every other character to an array lookup
  84. # 3 Escape constructs that are "special" to the tcl parser
  85. # 4 "subst" the result, doing all the array substitutions
  86.  
  87. proc CgiEncode {string} {
  88.     global UrlEncodeMap 
  89.     regsub -all \[^a-zA-Z0-9\] $string {$UrlEncodeMap(&)} string
  90.     regsub -all \n $string {\\n} string
  91.     regsub -all \t $string {\\t} string
  92.     regsub -all {[][{})\\]\)} $string {\\&} string
  93.     return [subst $string]
  94. }
  95. proc Url_Encode {string} {
  96.     CgiEncode $string
  97. }
  98.  
  99. proc Cgi_Value {key} {
  100.     global cgi
  101.     if [info exists cgi($key)] {
  102.     return $cgi($key)
  103.     } else {
  104.     return {}
  105.     }
  106. }
  107. proc Cgi_Header {title {bodyparams {}}} {
  108.     puts stdout \
  109. "Content-Type: text/html
  110.  
  111. <HTML>
  112. <Head>
  113. <title>$title</title>
  114. </Head>
  115. <Body $bodyparams>"
  116. }
  117. proc Cgi_Tail {} {
  118.     puts </Body>
  119. }
  120. proc Cgi_Redirect {url} {
  121.     puts stdout "\
  122. Content-type: text/html
  123. Location: $url
  124.  
  125. Please go to $url
  126. "
  127. }
  128. proc Cgi_CopyBits {file} {
  129.     if {![file exists $file] ||
  130.     [catch {open $file} in]} {
  131.     puts "Content-Type: text/html"
  132.     puts ""
  133.     puts "Cannot find file [file tail $file]"
  134.     exit 0
  135.     }
  136.     switch -- [file extension $file] {
  137.     ".hqx" {set type application/mac-binhex40}
  138.     default {set type application/octet-stream}
  139.     }
  140.     puts stdout "Content-Type: $type\nContent-Length: [file size $file]"
  141.     puts ""
  142.     fconfigure stdout -translation binary -buffering full -buffersize 8192
  143.     fconfigure $in -translation binary
  144.     copychannel $in stdout
  145.     close $in
  146. }
  147. proc H1 {str} {
  148.     Html_Tag H1 {} $str
  149. }
  150. proc H2 {str} {
  151.     Html_Tag H2 {} $str
  152. }
  153. proc H3 {str} {
  154.     Html_Tag H3 {} $str
  155. }
  156. proc H4 {str} {
  157.     Html_Tag H4 {} $str
  158. }
  159. proc H5 {str} {
  160.     Html_Tag H5 {} $str
  161. }
  162. proc H6 {str} {
  163.     Html_Tag H6 {} $str
  164. }
  165. proc P {} {
  166.     puts stdout <p>
  167. }
  168. proc Link {text href} {
  169.     puts "<a href=\"$href\">$text</a>"
  170. }
  171. proc Html_Tag {tag params str} {
  172.     puts stdout "<[string trim "$tag $params"]>$str</$tag>"
  173. }
  174. proc Form {url {method POST}} {
  175.     puts stdout "<form action=\"$url\" method=$method>"
  176. }
  177. proc Counter {filename} {
  178.     if [catch {open $filename} in] {
  179.     set number 0
  180.     } else {
  181.     set info [read $in]
  182.     close $in
  183.     if ![regexp {[0-9]+} $info number] {
  184.         return [clock seconds]    ;# Bail - race with file access
  185.     }
  186.     }
  187.     incr number
  188.     # Cannot open $filename.new because we likely won't
  189.     # have permission to create the temp file.
  190.     set out [open $filename w]
  191.     puts $out $number
  192.     close $out
  193.     return $number
  194. }
  195.  
  196. # Empty --
  197. #
  198. #    Return true if the variable doesn't exist or is an empty string
  199.  
  200. proc Empty {varname} {
  201.     upvar 1 $varname var
  202.     return [expr {![info exist var] || [string length $var] == 0}]
  203. }
  204.  
  205. # Cgi_SubstFile --
  206. # Use a file as a template
  207.  
  208. proc Cgi_SubstFile {path} {
  209.     if {[catch {open $path} in]} {
  210.     puts "<pre>Cgi_SubstFile: $path: $in</pre>"
  211.     } else {
  212.     set X [read $in]
  213.     close $in
  214.     puts [uplevel 1 [list subst $X]]
  215.     }
  216.     flush stdout
  217. }
  218.