Grammar and stream of consciousness cleanup for Channel and Scheduler rdoc
[rbx.git] / lib / cgi.rb
blobeb991cd2026ad39624678184edd70f4a731b454c
1
2 # cgi.rb - cgi support library
3
4 # Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
5
6 # Copyright (C) 2000  Information-technology Promotion Agency, Japan
8 # Author: Wakou Aoyama <wakou@ruby-lang.org>
10 # Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber) 
11
12 # == Overview
14 # The Common Gateway Interface (CGI) is a simple protocol
15 # for passing an HTTP request from a web server to a
16 # standalone program, and returning the output to the web
17 # browser.  Basically, a CGI program is called with the
18 # parameters of the request passed in either in the
19 # environment (GET) or via $stdin (POST), and everything
20 # it prints to $stdout is returned to the client.
21
22 # This file holds the +CGI+ class.  This class provides
23 # functionality for retrieving HTTP request parameters,
24 # managing cookies, and generating HTML output.  See the
25 # class documentation for more details and examples of use.
27 # The file cgi/session.rb provides session management
28 # functionality; see that file for more details.
30 # See http://www.w3.org/CGI/ for more information on the CGI
31 # protocol.
33 raise "Please, use ruby 1.5.4 or later." if RUBY_VERSION < "1.5.4"
35 require 'English'
37 # CGI class.  See documentation for the file cgi.rb for an overview
38 # of the CGI protocol.
40 # == Introduction
42 # CGI is a large class, providing several categories of methods, many of which
43 # are mixed in from other modules.  Some of the documentation is in this class,
44 # some in the modules CGI::QueryExtension and CGI::HtmlExtension.  See
45 # CGI::Cookie for specific information on handling cookies, and cgi/session.rb
46 # (CGI::Session) for information on sessions.
48 # For queries, CGI provides methods to get at environmental variables,
49 # parameters, cookies, and multipart request data.  For responses, CGI provides
50 # methods for writing output and generating HTML.
52 # Read on for more details.  Examples are provided at the bottom.
54 # == Queries
56 # The CGI class dynamically mixes in parameter and cookie-parsing
57 # functionality,  environmental variable access, and support for
58 # parsing multipart requests (including uploaded files) from the
59 # CGI::QueryExtension module.
61 # === Environmental Variables
63 # The standard CGI environmental variables are available as read-only
64 # attributes of a CGI object.  The following is a list of these variables:
67 #   AUTH_TYPE               HTTP_HOST          REMOTE_IDENT
68 #   CONTENT_LENGTH          HTTP_NEGOTIATE     REMOTE_USER
69 #   CONTENT_TYPE            HTTP_PRAGMA        REQUEST_METHOD
70 #   GATEWAY_INTERFACE       HTTP_REFERER       SCRIPT_NAME
71 #   HTTP_ACCEPT             HTTP_USER_AGENT    SERVER_NAME
72 #   HTTP_ACCEPT_CHARSET     PATH_INFO          SERVER_PORT
73 #   HTTP_ACCEPT_ENCODING    PATH_TRANSLATED    SERVER_PROTOCOL
74 #   HTTP_ACCEPT_LANGUAGE    QUERY_STRING       SERVER_SOFTWARE
75 #   HTTP_CACHE_CONTROL      REMOTE_ADDR
76 #   HTTP_FROM               REMOTE_HOST
79 # For each of these variables, there is a corresponding attribute with the
80 # same name, except all lower case and without a preceding HTTP_.  
81 # +content_length+ and +server_port+ are integers; the rest are strings.
83 # === Parameters
85 # The method #params() returns a hash of all parameters in the request as
86 # name/value-list pairs, where the value-list is an Array of one or more
87 # values.  The CGI object itself also behaves as a hash of parameter names 
88 # to values, but only returns a single value (as a String) for each 
89 # parameter name.
91 # For instance, suppose the request contains the parameter 
92 # "favourite_colours" with the multiple values "blue" and "green".  The
93 # following behaviour would occur:
95 #   cgi.params["favourite_colours"]  # => ["blue", "green"]
96 #   cgi["favourite_colours"]         # => "blue"
98 # If a parameter does not exist, the former method will return an empty
99 # array, the latter an empty string.  The simplest way to test for existence
100 # of a parameter is by the #has_key? method.
102 # === Cookies
104 # HTTP Cookies are automatically parsed from the request.  They are available
105 # from the #cookies() accessor, which returns a hash from cookie name to
106 # CGI::Cookie object.
108 # === Multipart requests
110 # If a request's method is POST and its content type is multipart/form-data, 
111 # then it may contain uploaded files.  These are stored by the QueryExtension
112 # module in the parameters of the request.  The parameter name is the name
113 # attribute of the file input field, as usual.  However, the value is not
114 # a string, but an IO object, either an IOString for small files, or a
115 # Tempfile for larger ones.  This object also has the additional singleton
116 # methods:
118 # #local_path():: the path of the uploaded file on the local filesystem
119 # #original_filename():: the name of the file on the client computer
120 # #content_type():: the content type of the file
122 # == Responses
124 # The CGI class provides methods for sending header and content output to
125 # the HTTP client, and mixes in methods for programmatic HTML generation
126 # from CGI::HtmlExtension and CGI::TagMaker modules.  The precise version of HTML
127 # to use for HTML generation is specified at object creation time.
129 # === Writing output
131 # The simplest way to send output to the HTTP client is using the #out() method.
132 # This takes the HTTP headers as a hash parameter, and the body content
133 # via a block.  The headers can be generated as a string using the #header()
134 # method.  The output stream can be written directly to using the #print()
135 # method.
137 # === Generating HTML
139 # Each HTML element has a corresponding method for generating that
140 # element as a String.  The name of this method is the same as that
141 # of the element, all lowercase.  The attributes of the element are 
142 # passed in as a hash, and the body as a no-argument block that evaluates
143 # to a String.  The HTML generation module knows which elements are
144 # always empty, and silently drops any passed-in body.  It also knows
145 # which elements require matching closing tags and which don't.  However,
146 # it does not know what attributes are legal for which elements.
148 # There are also some additional HTML generation methods mixed in from
149 # the CGI::HtmlExtension module.  These include individual methods for the
150 # different types of form inputs, and methods for elements that commonly
151 # take particular attributes where the attributes can be directly specified
152 # as arguments, rather than via a hash.
154 # == Examples of use
156 # === Get form values
158 #   require "cgi"
159 #   cgi = CGI.new
160 #   value = cgi['field_name']   # <== value string for 'field_name'
161 #     # if not 'field_name' included, then return "".
162 #   fields = cgi.keys            # <== array of field names
164 #   # returns true if form has 'field_name'
165 #   cgi.has_key?('field_name')
166 #   cgi.has_key?('field_name')
167 #   cgi.include?('field_name')
169 # CAUTION! cgi['field_name'] returned an Array with the old 
170 # cgi.rb(included in ruby 1.6)
172 # === Get form values as hash
174 #   require "cgi"
175 #   cgi = CGI.new
176 #   params = cgi.params
178 # cgi.params is a hash.
180 #   cgi.params['new_field_name'] = ["value"]  # add new param
181 #   cgi.params['field_name'] = ["new_value"]  # change value
182 #   cgi.params.delete('field_name')           # delete param
183 #   cgi.params.clear                          # delete all params
186 # === Save form values to file
188 #   require "pstore"
189 #   db = PStore.new("query.db")
190 #   db.transaction do
191 #     db["params"] = cgi.params
192 #   end
195 # === Restore form values from file
197 #   require "pstore"
198 #   db = PStore.new("query.db")
199 #   db.transaction do
200 #     cgi.params = db["params"]
201 #   end
204 # === Get multipart form values
206 #   require "cgi"
207 #   cgi = CGI.new
208 #   value = cgi['field_name']   # <== value string for 'field_name'
209 #   value.read                  # <== body of value
210 #   value.local_path            # <== path to local file of value
211 #   value.original_filename     # <== original filename of value
212 #   value.content_type          # <== content_type of value
214 # and value has StringIO or Tempfile class methods.
216 # === Get cookie values
218 #   require "cgi"
219 #   cgi = CGI.new
220 #   values = cgi.cookies['name']  # <== array of 'name'
221 #     # if not 'name' included, then return [].
222 #   names = cgi.cookies.keys      # <== array of cookie names
224 # and cgi.cookies is a hash.
226 # === Get cookie objects
228 #   require "cgi"
229 #   cgi = CGI.new
230 #   for name, cookie in cgi.cookies
231 #     cookie.expires = Time.now + 30
232 #   end
233 #   cgi.out("cookie" => cgi.cookies) {"string"}
235 #   cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }
237 #   require "cgi"
238 #   cgi = CGI.new
239 #   cgi.cookies['name'].expires = Time.now + 30
240 #   cgi.out("cookie" => cgi.cookies['name']) {"string"}
242 # === Print http header and html string to $DEFAULT_OUTPUT ($>)
244 #   require "cgi"
245 #   cgi = CGI.new("html3")  # add HTML generation methods
246 #   cgi.out() do
247 #     cgi.html() do
248 #       cgi.head{ cgi.title{"TITLE"} } +
249 #       cgi.body() do
250 #         cgi.form() do
251 #           cgi.textarea("get_text") +
252 #           cgi.br +
253 #           cgi.submit
254 #         end +
255 #         cgi.pre() do
256 #           CGI::escapeHTML(
257 #             "params: " + cgi.params.inspect + "\n" +
258 #             "cookies: " + cgi.cookies.inspect + "\n" +
259 #             ENV.collect() do |key, value|
260 #               key + " --> " + value + "\n"
261 #             end.join("")
262 #           )
263 #         end
264 #       end
265 #     end
266 #   end
268 #   # add HTML generation methods
269 #   CGI.new("html3")    # html3.2
270 #   CGI.new("html4")    # html4.01 (Strict)
271 #   CGI.new("html4Tr")  # html4.01 Transitional
272 #   CGI.new("html4Fr")  # html4.01 Frameset
274 class CGI
276   # :stopdoc:
278   # String for carriage return
279   CR  = "\015"
281   # String for linefeed
282   LF  = "\012"
284   # Standard internet newline sequence
285   EOL = CR + LF
287   REVISION = '$Id: cgi.rb 12050 2007-03-12 17:55:03Z knu $' #:nodoc:
289   NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM) 
291   # Path separators in different environments.
292   PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
294   # HTTP status codes.
295   HTTP_STATUS = {
296     "OK"                  => "200 OK",
297     "PARTIAL_CONTENT"     => "206 Partial Content",
298     "MULTIPLE_CHOICES"    => "300 Multiple Choices",
299     "MOVED"               => "301 Moved Permanently",
300     "REDIRECT"            => "302 Found",
301     "NOT_MODIFIED"        => "304 Not Modified",
302     "BAD_REQUEST"         => "400 Bad Request",
303     "AUTH_REQUIRED"       => "401 Authorization Required",
304     "FORBIDDEN"           => "403 Forbidden",
305     "NOT_FOUND"           => "404 Not Found",
306     "METHOD_NOT_ALLOWED"  => "405 Method Not Allowed",
307     "NOT_ACCEPTABLE"      => "406 Not Acceptable",
308     "LENGTH_REQUIRED"     => "411 Length Required",
309     "PRECONDITION_FAILED" => "412 Rrecondition Failed",
310     "SERVER_ERROR"        => "500 Internal Server Error",
311     "NOT_IMPLEMENTED"     => "501 Method Not Implemented",
312     "BAD_GATEWAY"         => "502 Bad Gateway",
313     "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
314   }
316   # Abbreviated day-of-week names specified by RFC 822
317   RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
319   # Abbreviated month names specified by RFC 822
320   RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
322   # :startdoc:
324   def env_table 
325     ENV
326   end
328   def stdinput
329     $stdin
330   end
332   def stdoutput
333     $DEFAULT_OUTPUT
334   end
336   private :env_table, :stdinput, :stdoutput
338   # URL-encode a string.
339   #   url_encoded_string = CGI::escape("'Stop!' said Fred")
340   #      # => "%27Stop%21%27+said+Fred"
341   def CGI::escape(string)
342     string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
343       '%' + $1.unpack('H2' * $1.size).join('%').upcase
344     end.tr(' ', '+')
345   end
348   # URL-decode a string.
349   #   string = CGI::unescape("%27Stop%21%27+said+Fred")
350   #      # => "'Stop!' said Fred"
351   def CGI::unescape(string)
352     string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
353       [$1.delete('%')].pack('H*')
354     end
355   end
358   # Escape special characters in HTML, namely &\"<>
359   #   CGI::escapeHTML('Usage: foo "bar" <baz>')
360   #      # => "Usage: foo &quot;bar&quot; &lt;baz&gt;"
361   def CGI::escapeHTML(string)
362     string.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
363   end
366   # Unescape a string that has been HTML-escaped
367   #   CGI::unescapeHTML("Usage: foo &quot;bar&quot; &lt;baz&gt;")
368   #      # => "Usage: foo \"bar\" <baz>"
369   def CGI::unescapeHTML(string)
370     string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/n) do
371       match = $1.dup
372       case match
373       when 'amp'                 then '&'
374       when 'quot'                then '"'
375       when 'gt'                  then '>'
376       when 'lt'                  then '<'
377       when /\A#0*(\d+)\z/n       then
378         if Integer($1) < 256
379           Integer($1).chr
380         else
381           if Integer($1) < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
382             [Integer($1)].pack("U")
383           else
384             "&##{$1};"
385           end
386         end
387       when /\A#x([0-9a-f]+)\z/ni then
388         if $1.hex < 256
389           $1.hex.chr
390         else
391           if $1.hex < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
392             [$1.hex].pack("U")
393           else
394             "&#x#{$1};"
395           end
396         end
397       else
398         "&#{match};"
399       end
400     end
401   end
404   # Escape only the tags of certain HTML elements in +string+.
405   #
406   # Takes an element or elements or array of elements.  Each element
407   # is specified by the name of the element, without angle brackets.
408   # This matches both the start and the end tag of that element.
409   # The attribute list of the open tag will also be escaped (for
410   # instance, the double-quotes surrounding attribute values).
411   #
412   #   print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
413   #     # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
414   #
415   #   print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
416   #     # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
417   def CGI::escapeElement(string, *elements)
418     elements = elements[0] if elements[0].kind_of?(Array)
419     unless elements.empty?
420       string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
421         CGI::escapeHTML($&)
422       end
423     else
424       string
425     end
426   end
429   # Undo escaping such as that done by CGI::escapeElement()
430   #
431   #   print CGI::unescapeElement(
432   #           CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
433   #     # "&lt;BR&gt;<A HREF="url"></A>"
434   # 
435   #   print CGI::unescapeElement(
436   #           CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
437   #     # "&lt;BR&gt;<A HREF="url"></A>"
438   def CGI::unescapeElement(string, *elements)
439     elements = elements[0] if elements[0].kind_of?(Array)
440     unless elements.empty?
441       string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/ni) do
442         CGI::unescapeHTML($&)
443       end
444     else
445       string
446     end
447   end
450   # Format a +Time+ object as a String using the format specified by RFC 1123.
451   #
452   #   CGI::rfc1123_date(Time.now)
453   #     # Sat, 01 Jan 2000 00:00:00 GMT
454   def CGI::rfc1123_date(time)
455     t = time.clone.gmtime
456     return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
457                 RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
458                 t.hour, t.min, t.sec)
459   end
462   # Create an HTTP header block as a string.
463   #
464   # Includes the empty line that ends the header block.
465   #
466   # +options+ can be a string specifying the Content-Type (defaults
467   # to text/html), or a hash of header key/value pairs.  The following
468   # header keys are recognized:
469   #
470   # type:: the Content-Type header.  Defaults to "text/html"
471   # charset:: the charset of the body, appended to the Content-Type header.
472   # nph:: a boolean value.  If true, prepend protocol string and status code, and
473   #       date; and sets default values for "server" and "connection" if not
474   #       explicitly set.
475   # status:: the HTTP status code, returned as the Status header.  See the
476   #          list of available status codes below.
477   # server:: the server software, returned as the Server header.
478   # connection:: the connection type, returned as the Connection header (for 
479   #              instance, "close".
480   # length:: the length of the content that will be sent, returned as the
481   #          Content-Length header.
482   # language:: the language of the content, returned as the Content-Language
483   #            header.
484   # expires:: the time on which the current content expires, as a +Time+
485   #           object, returned as the Expires header.
486   # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers.
487   #          The value can be the literal string of the cookie; a CGI::Cookie
488   #          object; an Array of literal cookie strings or Cookie objects; or a 
489   #          hash all of whose values are literal cookie strings or Cookie objects.
490   #          These cookies are in addition to the cookies held in the
491   #          @output_cookies field.
492   #
493   # Other header lines can also be set; they are appended as key: value.
494   # 
495   #   header
496   #     # Content-Type: text/html
497   # 
498   #   header("text/plain")
499   #     # Content-Type: text/plain
500   # 
501   #   header("nph"        => true,
502   #          "status"     => "OK",  # == "200 OK"
503   #            # "status"     => "200 GOOD",
504   #          "server"     => ENV['SERVER_SOFTWARE'],
505   #          "connection" => "close",
506   #          "type"       => "text/html",
507   #          "charset"    => "iso-2022-jp",
508   #            # Content-Type: text/html; charset=iso-2022-jp
509   #          "length"     => 103,
510   #          "language"   => "ja",
511   #          "expires"    => Time.now + 30,
512   #          "cookie"     => [cookie1, cookie2],
513   #          "my_header1" => "my_value"
514   #          "my_header2" => "my_value")
515   # 
516   # The status codes are:
517   # 
518   #   "OK"                  --> "200 OK"
519   #   "PARTIAL_CONTENT"     --> "206 Partial Content"
520   #   "MULTIPLE_CHOICES"    --> "300 Multiple Choices"
521   #   "MOVED"               --> "301 Moved Permanently"
522   #   "REDIRECT"            --> "302 Found"
523   #   "NOT_MODIFIED"        --> "304 Not Modified"
524   #   "BAD_REQUEST"         --> "400 Bad Request"
525   #   "AUTH_REQUIRED"       --> "401 Authorization Required"
526   #   "FORBIDDEN"           --> "403 Forbidden"
527   #   "NOT_FOUND"           --> "404 Not Found"
528   #   "METHOD_NOT_ALLOWED"  --> "405 Method Not Allowed"
529   #   "NOT_ACCEPTABLE"      --> "406 Not Acceptable"
530   #   "LENGTH_REQUIRED"     --> "411 Length Required"
531   #   "PRECONDITION_FAILED" --> "412 Precondition Failed"
532   #   "SERVER_ERROR"        --> "500 Internal Server Error"
533   #   "NOT_IMPLEMENTED"     --> "501 Method Not Implemented"
534   #   "BAD_GATEWAY"         --> "502 Bad Gateway"
535   #   "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"
536   # 
537   # This method does not perform charset conversion. 
538   #
539   def header(options = "text/html")
541     buf = ""
543     case options
544     when String
545       options = { "type" => options }
546     when Hash
547       options = options.dup
548     end
550     unless options.has_key?("type")
551       options["type"] = "text/html"
552     end
554     if options.has_key?("charset")
555       options["type"] += "; charset=" + options.delete("charset")
556     end
558     options.delete("nph") if defined?(MOD_RUBY)
559     if options.delete("nph") or
560         (/IIS\/(\d+)/n.match(env_table['SERVER_SOFTWARE']) and $1.to_i < 5)
561       buf += (env_table["SERVER_PROTOCOL"] or "HTTP/1.0")  + " " +
562              (HTTP_STATUS[options["status"]] or options["status"] or "200 OK") +
563              EOL +
564              "Date: " + CGI::rfc1123_date(Time.now) + EOL
566       unless options.has_key?("server")
567         options["server"] = (env_table['SERVER_SOFTWARE'] or "")
568       end
570       unless options.has_key?("connection")
571         options["connection"] = "close"
572       end
574       options.delete("status")
575     end
577     if options.has_key?("status")
578       buf += "Status: " +
579              (HTTP_STATUS[options["status"]] or options["status"]) + EOL
580       options.delete("status")
581     end
583     if options.has_key?("server")
584       buf += "Server: " + options.delete("server") + EOL
585     end
587     if options.has_key?("connection")
588       buf += "Connection: " + options.delete("connection") + EOL
589     end
591     buf += "Content-Type: " + options.delete("type") + EOL
593     if options.has_key?("length")
594       buf += "Content-Length: " + options.delete("length").to_s + EOL
595     end
597     if options.has_key?("language")
598       buf += "Content-Language: " + options.delete("language") + EOL
599     end
601     if options.has_key?("expires")
602       buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL
603     end
605     if options.has_key?("cookie")
606       if options["cookie"].kind_of?(String) or
607            options["cookie"].kind_of?(Cookie)
608         buf += "Set-Cookie: " + options.delete("cookie").to_s + EOL
609       elsif options["cookie"].kind_of?(Array)
610         options.delete("cookie").each{|cookie|
611           buf += "Set-Cookie: " + cookie.to_s + EOL
612         }
613       elsif options["cookie"].kind_of?(Hash)
614         options.delete("cookie").each_value{|cookie|
615           buf += "Set-Cookie: " + cookie.to_s + EOL
616         }
617       end
618     end
619     if @output_cookies
620       for cookie in @output_cookies
621         buf += "Set-Cookie: " + cookie.to_s + EOL
622       end
623     end
625     options.each{|key, value|
626       buf += key + ": " + value.to_s + EOL
627     }
629     if defined?(MOD_RUBY)
630       table = Apache::request.headers_out
631       buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
632         warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
633         case name
634         when 'Set-Cookie'
635           table.add(name, value)
636         when /^status$/ni
637           Apache::request.status_line = value
638           Apache::request.status = value.to_i
639         when /^content-type$/ni
640           Apache::request.content_type = value
641         when /^content-encoding$/ni
642           Apache::request.content_encoding = value
643         when /^location$/ni
644           if Apache::request.status == 200
645             Apache::request.status = 302
646           end
647           Apache::request.headers_out[name] = value
648         else
649           Apache::request.headers_out[name] = value
650         end
651       }
652       Apache::request.send_http_header
653       ''
654     else
655       buf + EOL
656     end
658   end # header()
661   # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
662   #
663   # The header is provided by +options+, as for #header().
664   # The body of the document is that returned by the passed-
665   # in block.  This block takes no arguments.  It is required.
666   #
667   #   cgi = CGI.new
668   #   cgi.out{ "string" }
669   #     # Content-Type: text/html
670   #     # Content-Length: 6
671   #     #
672   #     # string
673   # 
674   #   cgi.out("text/plain") { "string" }
675   #     # Content-Type: text/plain
676   #     # Content-Length: 6
677   #     #
678   #     # string
679   # 
680   #   cgi.out("nph"        => true,
681   #           "status"     => "OK",  # == "200 OK"
682   #           "server"     => ENV['SERVER_SOFTWARE'],
683   #           "connection" => "close",
684   #           "type"       => "text/html",
685   #           "charset"    => "iso-2022-jp",
686   #             # Content-Type: text/html; charset=iso-2022-jp
687   #           "language"   => "ja",
688   #           "expires"    => Time.now + (3600 * 24 * 30),
689   #           "cookie"     => [cookie1, cookie2],
690   #           "my_header1" => "my_value",
691   #           "my_header2" => "my_value") { "string" }
692   # 
693   # Content-Length is automatically calculated from the size of
694   # the String returned by the content block.
695   #
696   # If ENV['REQUEST_METHOD'] == "HEAD", then only the header
697   # is outputted (the content block is still required, but it
698   # is ignored).
699   # 
700   # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
701   # the content is converted to this charset, and the language is set 
702   # to "ja".
703   def out(options = "text/html") # :yield:
705     options = { "type" => options } if options.kind_of?(String)
706     content = yield
708     if options.has_key?("charset")
709       require "nkf"
710       case options["charset"]
711       when /iso-2022-jp/ni
712         content = NKF::nkf('-m0 -x -j', content)
713         options["language"] = "ja" unless options.has_key?("language")
714       when /euc-jp/ni
715         content = NKF::nkf('-m0 -x -e', content)
716         options["language"] = "ja" unless options.has_key?("language")
717       when /shift_jis/ni
718         content = NKF::nkf('-m0 -x -s', content)
719         options["language"] = "ja" unless options.has_key?("language")
720       end
721     end
723     options["length"] = content.length.to_s
724     output = stdoutput
725     output.binmode if defined? output.binmode
726     output.print header(options)
727     output.print content unless "HEAD" == env_table['REQUEST_METHOD']
728   end
731   # Print an argument or list of arguments to the default output stream
732   #
733   #   cgi = CGI.new
734   #   cgi.print    # default:  cgi.print == $DEFAULT_OUTPUT.print
735   def print(*options)
736     stdoutput.print(*options)
737   end
739   require "delegate"
741   # Class representing an HTTP cookie.
742   #
743   # In addition to its specific fields and methods, a Cookie instance
744   # is a delegator to the array of its values.
745   #
746   # See RFC 2965.
747   #
748   # == Examples of use
749   #   cookie1 = CGI::Cookie::new("name", "value1", "value2", ...)
750   #   cookie1 = CGI::Cookie::new("name" => "name", "value" => "value")
751   #   cookie1 = CGI::Cookie::new('name'    => 'name',
752   #                              'value'   => ['value1', 'value2', ...],
753   #                              'path'    => 'path',   # optional
754   #                              'domain'  => 'domain', # optional
755   #                              'expires' => Time.now, # optional
756   #                              'secure'  => true      # optional
757   #                             )
758   # 
759   #   cgi.out("cookie" => [cookie1, cookie2]) { "string" }
760   # 
761   #   name    = cookie1.name
762   #   values  = cookie1.value
763   #   path    = cookie1.path
764   #   domain  = cookie1.domain
765   #   expires = cookie1.expires
766   #   secure  = cookie1.secure
767   # 
768   #   cookie1.name    = 'name'
769   #   cookie1.value   = ['value1', 'value2', ...]
770   #   cookie1.path    = 'path'
771   #   cookie1.domain  = 'domain'
772   #   cookie1.expires = Time.now + 30
773   #   cookie1.secure  = true
774   class Cookie < DelegateClass(Array)
776     # Create a new CGI::Cookie object.
777     #
778     # The contents of the cookie can be specified as a +name+ and one
779     # or more +value+ arguments.  Alternatively, the contents can
780     # be specified as a single hash argument.  The possible keywords of
781     # this hash are as follows:
782     #
783     # name:: the name of the cookie.  Required.
784     # value:: the cookie's value or list of values.
785     # path:: the path for which this cookie applies.  Defaults to the
786     #        base directory of the CGI script.
787     # domain:: the domain for which this cookie applies.
788     # expires:: the time at which this cookie expires, as a +Time+ object.
789     # secure:: whether this cookie is a secure cookie or not (default to
790     #          false).  Secure cookies are only transmitted to HTTPS 
791     #          servers.
792     #
793     # These keywords correspond to attributes of the cookie object.
794     def initialize(name = "", *value)
795       options = if name.kind_of?(String)
796                   { "name" => name, "value" => value }
797                 else
798                   name
799                 end
800       unless options.has_key?("name")
801         raise ArgumentError, "`name' required"
802       end
804       @name = options["name"]
805       @value = Array(options["value"])
806       # simple support for IE
807       if options["path"]
808         @path = options["path"]
809       else
810         %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
811         @path = ($1 or "")
812       end
813       @domain = options["domain"]
814       @expires = options["expires"]
815       @secure = options["secure"] == true ? true : false
817       super(@value)
818     end
820     attr_accessor("name", "value", "path", "domain", "expires")
821     attr_reader("secure")
823     # Set whether the Cookie is a secure cookie or not.
824     #
825     # +val+ must be a boolean.
826     def secure=(val)
827       @secure = val if val == true or val == false
828       @secure
829     end
831     # Convert the Cookie to its string representation.
832     def to_s
833       buf = ""
834       buf += @name + '='
836       if @value.kind_of?(String)
837         buf += CGI::escape(@value)
838       else
839         buf += @value.collect{|v| CGI::escape(v) }.join("&")
840       end
842       if @domain
843         buf += '; domain=' + @domain
844       end
846       if @path
847         buf += '; path=' + @path
848       end
850       if @expires
851         buf += '; expires=' + CGI::rfc1123_date(@expires)
852       end
854       if @secure == true
855         buf += '; secure'
856       end
858       buf
859     end
861   end # class Cookie
864   # Parse a raw cookie string into a hash of cookie-name=>Cookie
865   # pairs.
866   #
867   #   cookies = CGI::Cookie::parse("raw_cookie_string")
868   #     # { "name1" => cookie1, "name2" => cookie2, ... }
869   #
870   def Cookie::parse(raw_cookie)
871     cookies = Hash.new([])
872     return cookies unless raw_cookie
874     raw_cookie.split(/[;,]\s?/).each do |pairs|
875       name, values = pairs.split('=',2)
876       next unless name and values
877       name = CGI::unescape(name)
878       values ||= ""
879       values = values.split('&').collect{|v| CGI::unescape(v) }
880       if cookies.has_key?(name)
881         values = cookies[name].value + values
882       end
883       cookies[name] = Cookie::new({ "name" => name, "value" => values })
884     end
886     cookies
887   end
889   # Parse an HTTP query string into a hash of key=>value pairs.
890   #
891   #   params = CGI::parse("query_string")
892   #     # {"name1" => ["value1", "value2", ...],
893   #     #  "name2" => ["value1", "value2", ...], ... }
894   #
895   def CGI::parse(query)
896     params = Hash.new([].freeze)
898     query.split(/[&;]/n).each do |pairs|
899       key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
900       if params.has_key?(key)
901         params[key].push(value)
902       else
903         params[key] = [value]
904       end
905     end
907     params
908   end
910   # Mixin module. It provides the follow functionality groups:
911   #
912   # 1. Access to CGI environment variables as methods.  See 
913   #    documentation to the CGI class for a list of these variables.
914   #
915   # 2. Access to cookies, including the cookies attribute.
916   #
917   # 3. Access to parameters, including the params attribute, and overloading
918   #    [] to perform parameter value lookup by key.
919   #
920   # 4. The initialize_query method, for initialising the above
921   #    mechanisms, handling multipart forms, and allowing the
922   #    class to be used in "offline" mode.
923   #
924   module QueryExtension
926     %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
927       define_method(env.sub(/^HTTP_/n, '').downcase) do
928         (val = env_table[env]) && Integer(val)
929       end
930     end
932     %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
933         PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
934         REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
935         SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
937         HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
938         HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
939         HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
940       define_method(env.sub(/^HTTP_/n, '').downcase) do
941         env_table[env]
942       end
943     end
945     # Get the raw cookies as a string.
946     def raw_cookie
947       env_table["HTTP_COOKIE"]
948     end
950     # Get the raw RFC2965 cookies as a string.
951     def raw_cookie2
952       env_table["HTTP_COOKIE2"]
953     end
955     # Get the cookies as a hash of cookie-name=>Cookie pairs.
956     attr_accessor("cookies")
958     # Get the parameters as a hash of name=>values pairs, where
959     # values is an Array.
960     attr("params")
962     # Set all the parameters.
963     def params=(hash)
964       @params.clear
965       @params.update(hash)
966     end
968     def read_multipart(boundary, content_length)
969       params = Hash.new([])
970       boundary = "--" + boundary
971       quoted_boundary = Regexp.quote(boundary, "n")
972       buf = ""
973       bufsize = 10 * 1024
974       boundary_end=""
976       # start multipart/form-data
977       stdinput.binmode if defined? stdinput.binmode
978       boundary_size = boundary.size + EOL.size
979       content_length -= boundary_size
980       status = stdinput.read(boundary_size)
981       if nil == status
982         raise EOFError, "no content body"
983       elsif boundary + EOL != status
984         raise EOFError, "bad content body"
985       end
987       loop do
988         head = nil
989         if 10240 < content_length
990           require "tempfile"
991           body = Tempfile.new("CGI")
992         else
993           begin
994             require "stringio"
995             body = StringIO.new
996           rescue LoadError
997             require "tempfile"
998             body = Tempfile.new("CGI")
999           end
1000         end
1001         body.binmode if defined? body.binmode
1003         until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
1005           if (not head) and /#{EOL}#{EOL}/n.match(buf)
1006             buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
1007               head = $1.dup
1008               ""
1009             end
1010             next
1011           end
1013           if head and ( (EOL + boundary + EOL).size < buf.size )
1014             body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
1015             buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
1016           end
1018           c = if bufsize < content_length
1019                 stdinput.read(bufsize)
1020               else
1021                 stdinput.read(content_length)
1022               end
1023           if c.nil? || c.empty?
1024             raise EOFError, "bad content body"
1025           end
1026           buf.concat(c)
1027           content_length -= c.size
1028         end
1030         buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
1031           body.print $1
1032           if "--" == $2
1033             content_length = -1
1034           end
1035          boundary_end = $2.dup
1036           ""
1037         end
1039         body.rewind
1041         /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni.match(head)
1042         filename = ($1 or $2 or "")
1043         if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
1044             /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
1045             (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
1046           filename = CGI::unescape(filename)
1047         end
1048         
1049         /Content-Type: (.*)/ni.match(head)
1050         content_type = ($1 or "")
1052         (class << body; self; end).class_eval do
1053           alias local_path path
1054           define_method(:original_filename) {filename.dup.taint}
1055           define_method(:content_type) {content_type.dup.taint}
1056         end
1058         /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
1059         name = $1.dup
1061         if params.has_key?(name)
1062           params[name].push(body)
1063         else
1064           params[name] = [body]
1065         end
1066         break if buf.size == 0
1067         break if content_length == -1
1068       end
1069       raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
1071       params
1072     end # read_multipart
1073     private :read_multipart
1075     # offline mode. read name=value pairs on standard input.
1076     def read_from_cmdline
1077       require "shellwords"
1079       string = unless ARGV.empty?
1080         ARGV.join(' ')
1081       else
1082         if STDIN.tty?
1083           STDERR.print(
1084             %|(offline mode: enter name=value pairs on standard input)\n|
1085           )
1086         end
1087         readlines.join(' ').gsub(/\n/n, '')
1088       end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
1090       words = Shellwords.shellwords(string)
1092       if words.find{|x| /=/n.match(x) }
1093         words.join('&')
1094       else
1095         words.join('+')
1096       end
1097     end
1098     private :read_from_cmdline
1100     # Initialize the data from the query.
1101     #
1102     # Handles multipart forms (in particular, forms that involve file uploads).
1103     # Reads query parameters in the @params field, and cookies into @cookies.
1104     def initialize_query()
1105       if ("POST" == env_table['REQUEST_METHOD']) and
1106          %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
1107         boundary = $1.dup
1108         @multipart = true
1109         @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
1110       else
1111         @multipart = false
1112         @params = CGI::parse(
1113                     case env_table['REQUEST_METHOD']
1114                     when "GET", "HEAD"
1115                       if defined?(MOD_RUBY)
1116                         Apache::request.args or ""
1117                       else
1118                         env_table['QUERY_STRING'] or ""
1119                       end
1120                     when "POST"
1121                       stdinput.binmode if defined? stdinput.binmode
1122                       stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
1123                     else
1124                       read_from_cmdline
1125                     end
1126                   )
1127       end
1129       @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
1130     end
1131     private :initialize_query
1133     def multipart?
1134       @multipart
1135     end
1137     module Value    # :nodoc:
1138       def set_params(params)
1139         @params = params
1140       end
1141       def [](idx, *args)
1142         if args.size == 0
1143           warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
1144           @params[idx]
1145         else
1146           super[idx,*args]
1147         end
1148       end
1149       def first
1150         warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
1151         self
1152       end
1153       alias last first
1154       def to_a
1155         @params || [self]
1156       end
1157       alias to_ary to_a         # to be rhs of multiple assignment
1158     end
1160     # Get the value for the parameter with a given key.
1161     #
1162     # If the parameter has multiple values, only the first will be 
1163     # retrieved; use #params() to get the array of values.
1164     def [](key)
1165       params = @params[key]
1166       value = params[0]
1167       if @multipart
1168         if value
1169           return value
1170         elsif defined? StringIO
1171           StringIO.new("")
1172         else
1173           Tempfile.new("CGI")
1174         end
1175       else
1176         str = if value then value.dup else "" end
1177         str.extend(Value)
1178         str.set_params(params)
1179         str
1180       end
1181     end
1183     # Return all parameter keys as an array.
1184     def keys(*args)
1185       @params.keys(*args)
1186     end
1188     # Returns true if a given parameter key exists in the query.
1189     def has_key?(*args)
1190       @params.has_key?(*args)
1191     end
1192     alias key? has_key?
1193     alias include? has_key?
1195   end # QueryExtension
1198   # Prettify (indent) an HTML string.
1199   #
1200   # +string+ is the HTML string to indent.  +shift+ is the indentation
1201   # unit to use; it defaults to two spaces.
1202   #
1203   #   print CGI::pretty("<HTML><BODY></BODY></HTML>")
1204   #     # <HTML>
1205   #     #   <BODY>
1206   #     #   </BODY>
1207   #     # </HTML>
1208   # 
1209   #   print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
1210   #     # <HTML>
1211   #     #         <BODY>
1212   #     #         </BODY>
1213   #     # </HTML>
1214   #
1215   def CGI::pretty(string, shift = "  ")
1216     lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
1217     end_pos = 0
1218     while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
1219       element = $1.dup
1220       start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos)
1221       lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__"
1222     end
1223     lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
1224   end
1227   # Base module for HTML-generation mixins.
1228   #
1229   # Provides methods for code generation for tags following
1230   # the various DTD element types.
1231   module TagMaker # :nodoc:
1233     # Generate code for an element with required start and end tags.
1234     #
1235     #   - -
1236     def nn_element_def(element)
1237       nOE_element_def(element, <<-END)
1238           if block_given?
1239             yield.to_s
1240           else
1241             ""
1242           end +
1243           "</#{element.upcase}>"
1244       END
1245     end
1247     # Generate code for an empty element.
1248     #
1249     #   - O EMPTY
1250     def nOE_element_def(element, append = nil)
1251       s = <<-END
1252           "<#{element.upcase}" + attributes.collect{|name, value|
1253             next unless value
1254             " " + CGI::escapeHTML(name) +
1255             if true == value
1256               ""
1257             else
1258               '="' + CGI::escapeHTML(value) + '"'
1259             end
1260           }.to_s + ">"
1261       END
1262       s.sub!(/\Z/, " +") << append if append
1263       s
1264     end
1266     # Generate code for an element for which the end (and possibly the
1267     # start) tag is optional.
1268     #
1269     #   O O or - O
1270     def nO_element_def(element)
1271       nOE_element_def(element, <<-END)
1272           if block_given?
1273             yield.to_s + "</#{element.upcase}>"
1274           else
1275             ""
1276           end
1277       END
1278     end
1280   end # TagMaker
1283   #
1284   # Mixin module providing HTML generation methods.
1285   #
1286   # For example,
1287   #   cgi.a("http://www.example.com") { "Example" }
1288   #     # => "<A HREF=\"http://www.example.com\">Example</A>"
1289   #
1290   # Modules Http3, Http4, etc., contain more basic HTML-generation methods
1291   # (:title, :center, etc.).
1292   #
1293   # See class CGI for a detailed example. 
1294   #
1295   module HtmlExtension
1298     # Generate an Anchor element as a string.
1299     #
1300     # +href+ can either be a string, giving the URL
1301     # for the HREF attribute, or it can be a hash of
1302     # the element's attributes.
1303     #
1304     # The body of the element is the string returned by the no-argument
1305     # block passed in.
1306     #
1307     #   a("http://www.example.com") { "Example" }
1308     #     # => "<A HREF=\"http://www.example.com\">Example</A>"
1309     #
1310     #   a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
1311     #     # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
1312     #
1313     def a(href = "") # :yield:
1314       attributes = if href.kind_of?(String)
1315                      { "HREF" => href }
1316                    else
1317                      href
1318                    end
1319       if block_given?
1320         super(attributes){ yield }
1321       else
1322         super(attributes)
1323       end
1324     end
1326     # Generate a Document Base URI element as a String. 
1327     #
1328     # +href+ can either by a string, giving the base URL for the HREF
1329     # attribute, or it can be a has of the element's attributes.
1330     #
1331     # The passed-in no-argument block is ignored.
1332     #
1333     #   base("http://www.example.com/cgi")
1334     #     # => "<BASE HREF=\"http://www.example.com/cgi\">"
1335     def base(href = "") # :yield:
1336       attributes = if href.kind_of?(String)
1337                      { "HREF" => href }
1338                    else
1339                      href
1340                    end
1341       if block_given?
1342         super(attributes){ yield }
1343       else
1344         super(attributes)
1345       end
1346     end
1348     # Generate a BlockQuote element as a string.
1349     #
1350     # +cite+ can either be a string, give the URI for the source of
1351     # the quoted text, or a hash, giving all attributes of the element,
1352     # or it can be omitted, in which case the element has no attributes.
1353     #
1354     # The body is provided by the passed-in no-argument block
1355     #
1356     #   blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
1357     #     #=> "<BLOCKQUOTE CITE=\"http://www.example.com/quotes/foo.html\">Foo!</BLOCKQUOTE>
1358     def blockquote(cite = nil)  # :yield:
1359       attributes = if cite.kind_of?(String)
1360                      { "CITE" => cite }
1361                    else
1362                      cite or ""
1363                    end
1364       if block_given?
1365         super(attributes){ yield }
1366       else
1367         super(attributes)
1368       end
1369     end
1372     # Generate a Table Caption element as a string.
1373     #
1374     # +align+ can be a string, giving the alignment of the caption
1375     # (one of top, bottom, left, or right).  It can be a hash of
1376     # all the attributes of the element.  Or it can be omitted.
1377     #
1378     # The body of the element is provided by the passed-in no-argument block.
1379     #
1380     #   caption("left") { "Capital Cities" }
1381     #     # => <CAPTION ALIGN=\"left\">Capital Cities</CAPTION>
1382     def caption(align = nil) # :yield:
1383       attributes = if align.kind_of?(String)
1384                      { "ALIGN" => align }
1385                    else
1386                      align or ""
1387                    end
1388       if block_given?
1389         super(attributes){ yield }
1390       else
1391         super(attributes)
1392       end
1393     end
1396     # Generate a Checkbox Input element as a string.
1397     #
1398     # The attributes of the element can be specified as three arguments,
1399     # +name+, +value+, and +checked+.  +checked+ is a boolean value;
1400     # if true, the CHECKED attribute will be included in the element.
1401     #
1402     # Alternatively, the attributes can be specified as a hash.
1403     #
1404     #   checkbox("name")
1405     #     # = checkbox("NAME" => "name")
1406     # 
1407     #   checkbox("name", "value")
1408     #     # = checkbox("NAME" => "name", "VALUE" => "value")
1409     # 
1410     #   checkbox("name", "value", true)
1411     #     # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true)
1412     def checkbox(name = "", value = nil, checked = nil)
1413       attributes = if name.kind_of?(String)
1414                      { "TYPE" => "checkbox", "NAME" => name,
1415                        "VALUE" => value, "CHECKED" => checked }
1416                    else
1417                      name["TYPE"] = "checkbox"
1418                      name
1419                    end
1420       input(attributes)
1421     end
1423     # Generate a sequence of checkbox elements, as a String.
1424     #
1425     # The checkboxes will all have the same +name+ attribute.
1426     # Each checkbox is followed by a label.
1427     # There will be one checkbox for each value.  Each value
1428     # can be specified as a String, which will be used both
1429     # as the value of the VALUE attribute and as the label
1430     # for that checkbox.  A single-element array has the
1431     # same effect.
1432     #
1433     # Each value can also be specified as a three-element array.
1434     # The first element is the VALUE attribute; the second is the
1435     # label; and the third is a boolean specifying whether this
1436     # checkbox is CHECKED.
1437     #
1438     # Each value can also be specified as a two-element
1439     # array, by omitting either the value element (defaults
1440     # to the same as the label), or the boolean checked element
1441     # (defaults to false).
1442     #
1443     #   checkbox_group("name", "foo", "bar", "baz")
1444     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
1445     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
1446     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
1447     # 
1448     #   checkbox_group("name", ["foo"], ["bar", true], "baz")
1449     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
1450     #     # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
1451     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
1452     # 
1453     #   checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1454     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
1455     #     # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
1456     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">Baz
1457     # 
1458     #   checkbox_group("NAME" => "name",
1459     #                    "VALUES" => ["foo", "bar", "baz"])
1460     # 
1461     #   checkbox_group("NAME" => "name",
1462     #                    "VALUES" => [["foo"], ["bar", true], "baz"])
1463     # 
1464     #   checkbox_group("NAME" => "name",
1465     #                    "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1466     def checkbox_group(name = "", *values)
1467       if name.kind_of?(Hash)
1468         values = name["VALUES"]
1469         name = name["NAME"]
1470       end
1471       values.collect{|value|
1472         if value.kind_of?(String)
1473           checkbox(name, value) + value
1474         else
1475           if value[value.size - 1] == true
1476             checkbox(name, value[0], true) +
1477             value[value.size - 2]
1478           else
1479             checkbox(name, value[0]) +
1480             value[value.size - 1]
1481           end
1482         end
1483       }.to_s
1484     end
1487     # Generate an File Upload Input element as a string.
1488     #
1489     # The attributes of the element can be specified as three arguments,
1490     # +name+, +size+, and +maxlength+.  +maxlength+ is the maximum length
1491     # of the file's _name_, not of the file's _contents_.
1492     #
1493     # Alternatively, the attributes can be specified as a hash.
1494     #
1495     # See #multipart_form() for forms that include file uploads.
1496     #
1497     #   file_field("name")
1498     #     # <INPUT TYPE="file" NAME="name" SIZE="20">
1499     # 
1500     #   file_field("name", 40)
1501     #     # <INPUT TYPE="file" NAME="name" SIZE="40">
1502     # 
1503     #   file_field("name", 40, 100)
1504     #     # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
1505     # 
1506     #   file_field("NAME" => "name", "SIZE" => 40)
1507     #     # <INPUT TYPE="file" NAME="name" SIZE="40">
1508     def file_field(name = "", size = 20, maxlength = nil)
1509       attributes = if name.kind_of?(String)
1510                      { "TYPE" => "file", "NAME" => name,
1511                        "SIZE" => size.to_s }
1512                    else
1513                      name["TYPE"] = "file"
1514                      name
1515                    end
1516       attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1517       input(attributes)
1518     end
1521     # Generate a Form element as a string.
1522     #
1523     # +method+ should be either "get" or "post", and defaults to the latter.
1524     # +action+ defaults to the current CGI script name.  +enctype+
1525     # defaults to "application/x-www-form-urlencoded".  
1526     #
1527     # Alternatively, the attributes can be specified as a hash.
1528     #
1529     # See also #multipart_form() for forms that include file uploads.
1530     #
1531     #   form{ "string" }
1532     #     # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1533     # 
1534     #   form("get") { "string" }
1535     #     # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1536     # 
1537     #   form("get", "url") { "string" }
1538     #     # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1539     # 
1540     #   form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" }
1541     #     # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
1542     def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
1543       attributes = if method.kind_of?(String)
1544                      { "METHOD" => method, "ACTION" => action,
1545                        "ENCTYPE" => enctype } 
1546                    else
1547                      unless method.has_key?("METHOD")
1548                        method["METHOD"] = "post"
1549                      end
1550                      unless method.has_key?("ENCTYPE")
1551                        method["ENCTYPE"] = enctype
1552                      end
1553                      method
1554                    end
1555       if block_given?
1556         body = yield
1557       else
1558         body = ""
1559       end
1560       if @output_hidden
1561         body += @output_hidden.collect{|k,v|
1562           "<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
1563         }.to_s
1564       end
1565       super(attributes){body}
1566     end
1568     # Generate a Hidden Input element as a string.
1569     #
1570     # The attributes of the element can be specified as two arguments,
1571     # +name+ and +value+.
1572     #
1573     # Alternatively, the attributes can be specified as a hash.
1574     #
1575     #   hidden("name")
1576     #     # <INPUT TYPE="hidden" NAME="name">
1577     # 
1578     #   hidden("name", "value")
1579     #     # <INPUT TYPE="hidden" NAME="name" VALUE="value">
1580     # 
1581     #   hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo")
1582     #     # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
1583     def hidden(name = "", value = nil)
1584       attributes = if name.kind_of?(String)
1585                      { "TYPE" => "hidden", "NAME" => name, "VALUE" => value }
1586                    else
1587                      name["TYPE"] = "hidden"
1588                      name
1589                    end
1590       input(attributes)
1591     end
1593     # Generate a top-level HTML element as a string.
1594     #
1595     # The attributes of the element are specified as a hash.  The
1596     # pseudo-attribute "PRETTY" can be used to specify that the generated
1597     # HTML string should be indented.  "PRETTY" can also be specified as
1598     # a string as the sole argument to this method.  The pseudo-attribute
1599     # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it
1600     # should include the entire text of this tag, including angle brackets.
1601     #
1602     # The body of the html element is supplied as a block.
1603     # 
1604     #   html{ "string" }
1605     #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
1606     # 
1607     #   html("LANG" => "ja") { "string" }
1608     #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
1609     # 
1610     #   html("DOCTYPE" => false) { "string" }
1611     #     # <HTML>string</HTML>
1612     # 
1613     #   html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
1614     #     # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
1615     # 
1616     #   html("PRETTY" => "  ") { "<BODY></BODY>" }
1617     #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1618     #     # <HTML>
1619     #     #   <BODY>
1620     #     #   </BODY>
1621     #     # </HTML>
1622     # 
1623     #   html("PRETTY" => "\t") { "<BODY></BODY>" }
1624     #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1625     #     # <HTML>
1626     #     #         <BODY>
1627     #     #         </BODY>
1628     #     # </HTML>
1629     # 
1630     #   html("PRETTY") { "<BODY></BODY>" }
1631     #     # = html("PRETTY" => "  ") { "<BODY></BODY>" }
1632     # 
1633     #   html(if $VERBOSE then "PRETTY" end) { "HTML string" }
1634     #
1635     def html(attributes = {}) # :yield:
1636       if nil == attributes
1637         attributes = {}
1638       elsif "PRETTY" == attributes
1639         attributes = { "PRETTY" => true }
1640       end
1641       pretty = attributes.delete("PRETTY")
1642       pretty = "  " if true == pretty
1643       buf = ""
1645       if attributes.has_key?("DOCTYPE")
1646         if attributes["DOCTYPE"]
1647           buf += attributes.delete("DOCTYPE")
1648         else
1649           attributes.delete("DOCTYPE")
1650         end
1651       else
1652         buf += doctype
1653       end
1655       if block_given?
1656         buf += super(attributes){ yield }
1657       else
1658         buf += super(attributes)
1659       end
1661       if pretty
1662         CGI::pretty(buf, pretty)
1663       else
1664         buf
1665       end
1667     end
1669     # Generate an Image Button Input element as a string.
1670     #
1671     # +src+ is the URL of the image to use for the button.  +name+ 
1672     # is the input name.  +alt+ is the alternative text for the image.
1673     #
1674     # Alternatively, the attributes can be specified as a hash.
1675     # 
1676     #   image_button("url")
1677     #     # <INPUT TYPE="image" SRC="url">
1678     # 
1679     #   image_button("url", "name", "string")
1680     #     # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
1681     # 
1682     #   image_button("SRC" => "url", "ATL" => "strng")
1683     #     # <INPUT TYPE="image" SRC="url" ALT="string">
1684     def image_button(src = "", name = nil, alt = nil)
1685       attributes = if src.kind_of?(String)
1686                      { "TYPE" => "image", "SRC" => src, "NAME" => name,
1687                        "ALT" => alt }
1688                    else
1689                      src["TYPE"] = "image"
1690                      src["SRC"] ||= ""
1691                      src
1692                    end
1693       input(attributes)
1694     end
1697     # Generate an Image element as a string.
1698     #
1699     # +src+ is the URL of the image.  +alt+ is the alternative text for
1700     # the image.  +width+ is the width of the image, and +height+ is
1701     # its height.
1702     #
1703     # Alternatively, the attributes can be specified as a hash.
1704     #
1705     #   img("src", "alt", 100, 50)
1706     #     # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
1707     # 
1708     #   img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50)
1709     #     # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
1710     def img(src = "", alt = "", width = nil, height = nil)
1711       attributes = if src.kind_of?(String)
1712                      { "SRC" => src, "ALT" => alt }
1713                    else
1714                      src
1715                    end
1716       attributes["WIDTH"] = width.to_s if width
1717       attributes["HEIGHT"] = height.to_s if height
1718       super(attributes)
1719     end
1722     # Generate a Form element with multipart encoding as a String.
1723     #
1724     # Multipart encoding is used for forms that include file uploads.
1725     #
1726     # +action+ is the action to perform.  +enctype+ is the encoding
1727     # type, which defaults to "multipart/form-data".
1728     #
1729     # Alternatively, the attributes can be specified as a hash.
1730     #
1731     #   multipart_form{ "string" }
1732     #     # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
1733     # 
1734     #   multipart_form("url") { "string" }
1735     #     # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
1736     def multipart_form(action = nil, enctype = "multipart/form-data")
1737       attributes = if action == nil
1738                      { "METHOD" => "post", "ENCTYPE" => enctype } 
1739                    elsif action.kind_of?(String)
1740                      { "METHOD" => "post", "ACTION" => action,
1741                        "ENCTYPE" => enctype } 
1742                    else
1743                      unless action.has_key?("METHOD")
1744                        action["METHOD"] = "post"
1745                      end
1746                      unless action.has_key?("ENCTYPE")
1747                        action["ENCTYPE"] = enctype
1748                      end
1749                      action
1750                    end
1751       if block_given?
1752         form(attributes){ yield }
1753       else
1754         form(attributes)
1755       end
1756     end
1759     # Generate a Password Input element as a string.
1760     #
1761     # +name+ is the name of the input field.  +value+ is its default
1762     # value.  +size+ is the size of the input field display.  +maxlength+
1763     # is the maximum length of the inputted password.
1764     #
1765     # Alternatively, attributes can be specified as a hash.
1766     #
1767     #   password_field("name")
1768     #     # <INPUT TYPE="password" NAME="name" SIZE="40">
1769     # 
1770     #   password_field("name", "value")
1771     #     # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
1772     # 
1773     #   password_field("password", "value", 80, 200)
1774     #     # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
1775     # 
1776     #   password_field("NAME" => "name", "VALUE" => "value")
1777     #     # <INPUT TYPE="password" NAME="name" VALUE="value">
1778     def password_field(name = "", value = nil, size = 40, maxlength = nil)
1779       attributes = if name.kind_of?(String)
1780                      { "TYPE" => "password", "NAME" => name,
1781                        "VALUE" => value, "SIZE" => size.to_s }
1782                    else
1783                      name["TYPE"] = "password"
1784                      name
1785                    end
1786       attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1787       input(attributes)
1788     end
1790     # Generate a Select element as a string.
1791     #
1792     # +name+ is the name of the element.  The +values+ are the options that
1793     # can be selected from the Select menu.  Each value can be a String or
1794     # a one, two, or three-element Array.  If a String or a one-element
1795     # Array, this is both the value of that option and the text displayed for
1796     # it.  If a three-element Array, the elements are the option value, displayed
1797     # text, and a boolean value specifying whether this option starts as selected.
1798     # The two-element version omits either the option value (defaults to the same
1799     # as the display text) or the boolean selected specifier (defaults to false).
1800     #
1801     # The attributes and options can also be specified as a hash.  In this
1802     # case, options are specified as an array of values as described above,
1803     # with the hash key of "VALUES".
1804     #
1805     #   popup_menu("name", "foo", "bar", "baz")
1806     #     # <SELECT NAME="name">
1807     #     #   <OPTION VALUE="foo">foo</OPTION>
1808     #     #   <OPTION VALUE="bar">bar</OPTION>
1809     #     #   <OPTION VALUE="baz">baz</OPTION>
1810     #     # </SELECT>
1811     # 
1812     #   popup_menu("name", ["foo"], ["bar", true], "baz")
1813     #     # <SELECT NAME="name">
1814     #     #   <OPTION VALUE="foo">foo</OPTION>
1815     #     #   <OPTION VALUE="bar" SELECTED>bar</OPTION>
1816     #     #   <OPTION VALUE="baz">baz</OPTION>
1817     #     # </SELECT>
1818     # 
1819     #   popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1820     #     # <SELECT NAME="name">
1821     #     #   <OPTION VALUE="1">Foo</OPTION>
1822     #     #   <OPTION SELECTED VALUE="2">Bar</OPTION>
1823     #     #   <OPTION VALUE="Baz">Baz</OPTION>
1824     #     # </SELECT>
1825     # 
1826     #   popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
1827     #               "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1828     #     # <SELECT NAME="name" MULTIPLE SIZE="2">
1829     #     #   <OPTION VALUE="1">Foo</OPTION>
1830     #     #   <OPTION SELECTED VALUE="2">Bar</OPTION>
1831     #     #   <OPTION VALUE="Baz">Baz</OPTION>
1832     #     # </SELECT>
1833     def popup_menu(name = "", *values)
1835       if name.kind_of?(Hash)
1836         values   = name["VALUES"]
1837         size     = name["SIZE"].to_s if name["SIZE"]
1838         multiple = name["MULTIPLE"]
1839         name     = name["NAME"]
1840       else
1841         size = nil
1842         multiple = nil
1843       end
1845       select({ "NAME" => name, "SIZE" => size,
1846                "MULTIPLE" => multiple }){
1847         values.collect{|value|
1848           if value.kind_of?(String)
1849             option({ "VALUE" => value }){ value }
1850           else
1851             if value[value.size - 1] == true
1852               option({ "VALUE" => value[0], "SELECTED" => true }){
1853                 value[value.size - 2]
1854               }
1855             else
1856               option({ "VALUE" => value[0] }){
1857                 value[value.size - 1]
1858               }
1859             end
1860           end
1861         }.to_s
1862       }
1864     end
1866     # Generates a radio-button Input element.
1867     #
1868     # +name+ is the name of the input field.  +value+ is the value of
1869     # the field if checked.  +checked+ specifies whether the field
1870     # starts off checked.
1871     #
1872     # Alternatively, the attributes can be specified as a hash.
1873     #
1874     #   radio_button("name", "value")
1875     #     # <INPUT TYPE="radio" NAME="name" VALUE="value">
1876     # 
1877     #   radio_button("name", "value", true)
1878     #     # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
1879     # 
1880     #   radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo")
1881     #     # <INPUT TYPE="radio" NAME="name" VALUE="value" ID="foo">
1882     def radio_button(name = "", value = nil, checked = nil)
1883       attributes = if name.kind_of?(String)
1884                      { "TYPE" => "radio", "NAME" => name,
1885                        "VALUE" => value, "CHECKED" => checked }
1886                    else
1887                      name["TYPE"] = "radio"
1888                      name
1889                    end
1890       input(attributes)
1891     end
1893     # Generate a sequence of radio button Input elements, as a String.
1894     #
1895     # This works the same as #checkbox_group().  However, it is not valid
1896     # to have more than one radiobutton in a group checked.
1897     # 
1898     #   radio_group("name", "foo", "bar", "baz")
1899     #     # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
1900     #     # <INPUT TYPE="radio" NAME="name" VALUE="bar">bar
1901     #     # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
1902     # 
1903     #   radio_group("name", ["foo"], ["bar", true], "baz")
1904     #     # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
1905     #     # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="bar">bar
1906     #     # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
1907     # 
1908     #   radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1909     #     # <INPUT TYPE="radio" NAME="name" VALUE="1">Foo
1910     #     # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="2">Bar
1911     #     # <INPUT TYPE="radio" NAME="name" VALUE="Baz">Baz
1912     # 
1913     #   radio_group("NAME" => "name",
1914     #                 "VALUES" => ["foo", "bar", "baz"])
1915     # 
1916     #   radio_group("NAME" => "name",
1917     #                 "VALUES" => [["foo"], ["bar", true], "baz"])
1918     # 
1919     #   radio_group("NAME" => "name",
1920     #                 "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1921     def radio_group(name = "", *values)
1922       if name.kind_of?(Hash)
1923         values = name["VALUES"]
1924         name = name["NAME"]
1925       end
1926       values.collect{|value|
1927         if value.kind_of?(String)
1928           radio_button(name, value) + value
1929         else
1930           if value[value.size - 1] == true
1931             radio_button(name, value[0], true) +
1932             value[value.size - 2]
1933           else
1934             radio_button(name, value[0]) +
1935             value[value.size - 1]
1936           end
1937         end
1938       }.to_s
1939     end
1941     # Generate a reset button Input element, as a String.
1942     #
1943     # This resets the values on a form to their initial values.  +value+
1944     # is the text displayed on the button. +name+ is the name of this button.
1945     #
1946     # Alternatively, the attributes can be specified as a hash.
1947     #
1948     #   reset
1949     #     # <INPUT TYPE="reset">
1950     # 
1951     #   reset("reset")
1952     #     # <INPUT TYPE="reset" VALUE="reset">
1953     # 
1954     #   reset("VALUE" => "reset", "ID" => "foo")
1955     #     # <INPUT TYPE="reset" VALUE="reset" ID="foo">
1956     def reset(value = nil, name = nil)
1957       attributes = if (not value) or value.kind_of?(String)
1958                      { "TYPE" => "reset", "VALUE" => value, "NAME" => name }
1959                    else
1960                      value["TYPE"] = "reset"
1961                      value
1962                    end
1963       input(attributes)
1964     end
1966     alias scrolling_list popup_menu
1968     # Generate a submit button Input element, as a String.
1969     #
1970     # +value+ is the text to display on the button.  +name+ is the name
1971     # of the input.
1972     #
1973     # Alternatively, the attributes can be specified as a hash.
1974     #
1975     #   submit
1976     #     # <INPUT TYPE="submit">
1977     # 
1978     #   submit("ok")
1979     #     # <INPUT TYPE="submit" VALUE="ok">
1980     # 
1981     #   submit("ok", "button1")
1982     #     # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
1983     # 
1984     #   submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo")
1985     #     # <INPUT TYPE="submit" VALUE="ok" NAME="button1" ID="foo">
1986     def submit(value = nil, name = nil)
1987       attributes = if (not value) or value.kind_of?(String)
1988                      { "TYPE" => "submit", "VALUE" => value, "NAME" => name }
1989                    else
1990                      value["TYPE"] = "submit"
1991                      value
1992                    end
1993       input(attributes)
1994     end
1996     # Generate a text field Input element, as a String.
1997     #
1998     # +name+ is the name of the input field.  +value+ is its initial
1999     # value.  +size+ is the size of the input area.  +maxlength+
2000     # is the maximum length of input accepted.
2001     #
2002     # Alternatively, the attributes can be specified as a hash.
2003     #
2004     #   text_field("name")
2005     #     # <INPUT TYPE="text" NAME="name" SIZE="40">
2006     # 
2007     #   text_field("name", "value")
2008     #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
2009     # 
2010     #   text_field("name", "value", 80)
2011     #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
2012     # 
2013     #   text_field("name", "value", 80, 200)
2014     #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
2015     # 
2016     #   text_field("NAME" => "name", "VALUE" => "value")
2017     #     # <INPUT TYPE="text" NAME="name" VALUE="value">
2018     def text_field(name = "", value = nil, size = 40, maxlength = nil)
2019       attributes = if name.kind_of?(String)
2020                      { "TYPE" => "text", "NAME" => name, "VALUE" => value,
2021                        "SIZE" => size.to_s }
2022                    else
2023                      name["TYPE"] = "text"
2024                      name
2025                    end
2026       attributes["MAXLENGTH"] = maxlength.to_s if maxlength
2027       input(attributes)
2028     end
2030     # Generate a TextArea element, as a String.
2031     #
2032     # +name+ is the name of the textarea.  +cols+ is the number of
2033     # columns and +rows+ is the number of rows in the display.
2034     #
2035     # Alternatively, the attributes can be specified as a hash.
2036     #
2037     # The body is provided by the passed-in no-argument block
2038     #
2039     #   textarea("name")
2040     #      # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10)
2041     #
2042     #   textarea("name", 40, 5)
2043     #      # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5)
2044     def textarea(name = "", cols = 70, rows = 10)  # :yield:
2045       attributes = if name.kind_of?(String)
2046                      { "NAME" => name, "COLS" => cols.to_s,
2047                        "ROWS" => rows.to_s }
2048                    else
2049                      name
2050                    end
2051       if block_given?
2052         super(attributes){ yield }
2053       else
2054         super(attributes)
2055       end
2056     end
2058   end # HtmlExtension
2061   # Mixin module for HTML version 3 generation methods.
2062   module Html3 # :nodoc:
2064     # The DOCTYPE declaration for this version of HTML
2065     def doctype
2066       %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
2067     end
2069     # Initialise the HTML generation methods for this version.
2070     def element_init
2071       extend TagMaker
2072       methods = ""
2073       # - -
2074       for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
2075           DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
2076           APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
2077           STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
2078           CAPTION ]
2079         methods += <<-BEGIN + nn_element_def(element) + <<-END
2080           def #{element.downcase}(attributes = {})
2081         BEGIN
2082           end
2083         END
2084       end
2086       # - O EMPTY
2087       for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
2088           ISINDEX META ]
2089         methods += <<-BEGIN + nOE_element_def(element) + <<-END
2090           def #{element.downcase}(attributes = {})
2091         BEGIN
2092           end
2093         END
2094       end
2096       # O O or - O
2097       for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
2098           th td ]
2099         methods += <<-BEGIN + nO_element_def(element) + <<-END
2100           def #{element.downcase}(attributes = {})
2101         BEGIN
2102           end
2103         END
2104       end
2105       eval(methods)
2106     end
2108   end # Html3
2111   # Mixin module for HTML version 4 generation methods.
2112   module Html4 # :nodoc:
2114     # The DOCTYPE declaration for this version of HTML
2115     def doctype
2116       %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
2117     end
2119     # Initialise the HTML generation methods for this version.
2120     def element_init
2121       extend TagMaker
2122       methods = ""
2123       # - -
2124       for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD
2125         VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT
2126         H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP
2127         FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT
2128         TEXTAREA FORM A BLOCKQUOTE CAPTION ]
2129         methods += <<-BEGIN + nn_element_def(element) + <<-END
2130           def #{element.downcase}(attributes = {})
2131         BEGIN
2132           end
2133         END
2134       end
2136       # - O EMPTY
2137       for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]
2138         methods += <<-BEGIN + nOE_element_def(element) + <<-END
2139           def #{element.downcase}(attributes = {})
2140         BEGIN
2141           end
2142         END
2143       end
2145       # O O or - O
2146       for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
2147           COLGROUP TR TH TD HEAD]
2148         methods += <<-BEGIN + nO_element_def(element) + <<-END
2149           def #{element.downcase}(attributes = {})
2150         BEGIN
2151           end
2152         END
2153       end
2154       eval(methods)
2155     end
2157   end # Html4
2160   # Mixin module for HTML version 4 transitional generation methods.
2161   module Html4Tr # :nodoc:
2163     # The DOCTYPE declaration for this version of HTML
2164     def doctype
2165       %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
2166     end
2168     # Initialise the HTML generation methods for this version.
2169     def element_init
2170       extend TagMaker
2171       methods = ""
2172       # - -
2173       for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN
2174           CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO
2175           ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q
2176           INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET
2177           LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT
2178           NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]
2179         methods += <<-BEGIN + nn_element_def(element) + <<-END
2180           def #{element.downcase}(attributes = {})
2181         BEGIN
2182           end
2183         END
2184       end
2186       # - O EMPTY
2187       for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
2188           COL ISINDEX META ]
2189         methods += <<-BEGIN + nOE_element_def(element) + <<-END
2190           def #{element.downcase}(attributes = {})
2191         BEGIN
2192           end
2193         END
2194       end
2196       # O O or - O
2197       for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
2198           COLGROUP TR TH TD HEAD ]
2199         methods += <<-BEGIN + nO_element_def(element) + <<-END
2200           def #{element.downcase}(attributes = {})
2201         BEGIN
2202           end
2203         END
2204       end
2205       eval(methods)
2206     end
2208   end # Html4Tr
2211   # Mixin module for generating HTML version 4 with framesets.
2212   module Html4Fr # :nodoc:
2214     # The DOCTYPE declaration for this version of HTML
2215     def doctype
2216       %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
2217     end
2219     # Initialise the HTML generation methods for this version.
2220     def element_init
2221       methods = ""
2222       # - -
2223       for element in %w[ FRAMESET ]
2224         methods += <<-BEGIN + nn_element_def(element) + <<-END
2225           def #{element.downcase}(attributes = {})
2226         BEGIN
2227           end
2228         END
2229       end
2231       # - O EMPTY
2232       for element in %w[ FRAME ]
2233         methods += <<-BEGIN + nOE_element_def(element) + <<-END
2234           def #{element.downcase}(attributes = {})
2235         BEGIN
2236           end
2237         END
2238       end
2239       eval(methods)
2240     end
2242   end # Html4Fr
2245   # Creates a new CGI instance.
2246   #
2247   # +type+ specifies which version of HTML to load the HTML generation
2248   # methods for.  The following versions of HTML are supported:
2249   #
2250   # html3:: HTML 3.x
2251   # html4:: HTML 4.0
2252   # html4Tr:: HTML 4.0 Transitional
2253   # html4Fr:: HTML 4.0 with Framesets
2254   #
2255   # If not specified, no HTML generation methods will be loaded.
2256   #
2257   # If the CGI object is not created in a standard CGI call environment
2258   # (that is, it can't locate REQUEST_METHOD in its environment), then
2259   # it will run in "offline" mode.  In this mode, it reads its parameters
2260   # from the command line or (failing that) from standard input.  Otherwise,
2261   # cookies and other parameters are parsed automatically from the standard
2262   # CGI locations, which varies according to the REQUEST_METHOD.
2263   def initialize(type = "query")
2264     if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
2265       Apache.request.setup_cgi_env
2266     end
2268     extend QueryExtension
2269     @multipart = false
2270     if defined?(CGI_PARAMS)
2271       warn "do not use CGI_PARAMS and CGI_COOKIES"
2272       @params = CGI_PARAMS.dup
2273       @cookies = CGI_COOKIES.dup
2274     else
2275       initialize_query()  # set @params, @cookies
2276     end
2277     @output_cookies = nil
2278     @output_hidden = nil
2280     case type
2281     when "html3"
2282       extend Html3
2283       element_init()
2284       extend HtmlExtension
2285     when "html4"
2286       extend Html4
2287       element_init()
2288       extend HtmlExtension
2289     when "html4Tr"
2290       extend Html4Tr
2291       element_init()
2292       extend HtmlExtension
2293     when "html4Fr"
2294       extend Html4Tr
2295       element_init()
2296       extend Html4Fr
2297       element_init()
2298       extend HtmlExtension
2299     end
2300   end
2302 end   # class CGI