From a9e9697994a08600f5dbb46a1fe2a07233cb4890 Mon Sep 17 00:00:00 2001 From: matz Date: Thu, 4 Nov 1999 08:39:57 +0000 Subject: 19991104 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@557 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/cgi.rb | 1949 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/jcode.rb | 111 ++-- 2 files changed, 2024 insertions(+), 36 deletions(-) create mode 100644 lib/cgi.rb (limited to 'lib') diff --git a/lib/cgi.rb b/lib/cgi.rb new file mode 100644 index 0000000000..fa98fecc61 --- /dev/null +++ b/lib/cgi.rb @@ -0,0 +1,1949 @@ +=begin +$Date$ + +== CGI SUPPORT LIBRARY + +CGI.rb + +Version 1.00 + +Copyright (C) 1999 Network Applied Communication Laboratory, Inc. + +Wakou Aoyama + + +== EXAMPLE + +=== GET FORM VALUES + + require "cgi" + cgi = CGI.new + values = cgi['field_name'] # <== array of 'field_name' + # if not 'field_name' included, then return []. + fields = cgi.keys # <== array of field names + + # returns true if form has 'field_name' + cgi.has_key?('field_name') + cgi.key?('field_name') + cgi.include?('field_name') + + +=== GET FORM VALUES AS HASH + + require "cgi" + cgi = CGI.new + params = cgi.params + +cgi.params is a hash. + + cgi.params['new_field_name'] = ["value"] # add new param + cgi.params['field_name'] = ["new_value"] # change value + cgi.params.delete('field_name') # delete param + cgi.params.clear # delete all params + + +=== SAVE FORM VALUES TO FILE + + require "pstore" + db = PStore.new("query.db") + db.transaction do + db["params"] = cgi.params + end + + +=== RESTORE FORM VALUES FROM FILE + + require "pstore" + db = PStore.new("query.db") + db.transaction do + cgi.params = db["params"] + end + + +=== GET MULTIPART FORM VALUES + + require "cgi" + cgi = CGI.new + values = cgi['field_name'] # <== array of 'field_name' + values[0].read # <== body of values[0] + values[0].local_path # <== path to local file of values[0] + values[0].original_filename # <== original filename of values[0] + values[0].content_type # <== content_type of values[0] + +and values[0] has Tempfile class methods. + +(Tempfile class object has File class methods) + + +=== GET COOKIE VALUES + + require "cgi" + cgi = CGI.new + values = cgi.cookies['name'] # <== array of 'name' + # if not 'name' included, then return []. + names = cgi.cookies.keys # <== array of cookie names + +and cgi.cookies is a hash. + + +=== GET COOKIE OBJECTS + + require "cgi" + cgi = CGI.new + for name, cookie in cgi.cookies + cookie.expires = Time.now + 30 + end + cgi.out({ "cookie" => cgi.cookies }){} + + cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... } + + require "cgi" + cgi = CGI.new + cgi.cookies['name'].expires = Time.now + 30 + cgi.out({ "cookie" => cgi.cookies['name'] }){} + +and see MAKE COOKIE OBJECT. + + +=== GET ENVIRONMENT VALUE + + require "CGI" + cgi = CGI.new + value = cgi.auth_type + # ENV["AUTH_TYPE"] + +http://www.w3.org/CGI/ + +AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO +PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT +REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PORT +SERVER_PROTOCOL SERVER_SOFTWARE + +content_length and server_port return Integer. and the others return String. + +and HTTP_COOKIE, HTTP_COOKIE2 + + value = cgi.raw_cookie + # ENV["HTTP_COOKIE"] + value = cgi.raw_cookie2 + # ENV["HTTP_COOKIE2"] + +and other HTTP_* + + value = cgi.accept + # ENV["HTTP_ACCEPT"] + value = cgi.accept_charset + # ENV["HTTP_ACCEPT_CHARSET"] + +HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE +HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST HTTP_NEGOTIATE HTTP_PRAGMA +HTTP_REFERER HTTP_USER_AGENT + + +=== PRINT HTTP HEADER AND HTML STRING TO $> + + require "cgi" + cgi = CGI.new("html3") # add HTML generation methods + cgi.out{ + cgi.html{ + cgi.head{ cgi.title{"TITLE"} } + + cgi.body{ + cgi.form{ + cgi.textarea("get_text") + + cgi.br + + cgi.submit + } + + cgi.pre{ + CGI::escapeHTML( + "params: " + cgi.params.inspect + "\n" + + "cookies: " + cgi.cookies.inspect + "\n" + + ENV.collect{|key, value| + key + " --> " + value + "\n" + }.to_s + ) + } + } + } + } + + # add HTML generation methods + CGI.new("html3") # html3.2 + CGI.new("html4") # html4.0 (Strict) + CGI.new("html4Tr") # html4.0 Transitional + CGI.new("html4Fr") # html4.0 Frameset + +=end + + +class CGI + + CR = "\015" + LF = "\012" + EOL = CR + LF +v = $-v +$-v = false + VERSION = "1.00" + RELEASE_DATE = "$Date$" +$-v = v + + NEEDS_BINMODE = true if /WIN/ni === PLATFORM + PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'} + + HTTP_STATUS = { + "OK" => "200 OK", + "PARTIAL_CONTENT" => "206 Partial Content", + "MULTIPLE_CHOICES" => "300 Multiple Choices", + "MOVED" => "301 Moved Permanently", + "REDIRECT" => "302 Found", + "NOT_MODIFIED" => "304 Not Modified", + "BAD_REQUEST" => "400 Bad Request", + "AUTH_REQUIRED" => "401 Authorization Required", + "FORBIDDEN" => "403 Forbidden", + "NOT_FOUND" => "404 Not Found", + "METHOD_NOT_ALLOWED" => "405 Method Not Allowed", + "NOT_ACCEPTABLE" => "406 Not Acceptable", + "LENGTH_REQUIRED" => "411 Length Required", + "PRECONDITION_FAILED" => "412 Rrecondition Failed", + "SERVER_ERROR" => "500 Internal Server Error", + "NOT_IMPLEMENTED" => "501 Method Not Implemented", + "BAD_GATEWAY" => "502 Bad Gateway", + "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates" + } + + RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ] + RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ] + + def env_table + ENV + end + + def stdinput + $stdin + end + + def stdoutput + $> + end + + private :env_table, :stdinput, :stdoutput + +=begin +== METHODS +=end + +=begin +=== ESCAPE URL ENCODE + url_encoded_string = CGI::escape("string") +=end + def CGI::escape(string) + str = string.dup + str.gsub!(/[^a-zA-Z0-9_.-]/n){ sprintf("%%%02X", $&.unpack("C")[0]) } + str + end + + +=begin +=== UNESCAPE URL ENCODED + string = CGI::unescape("url encoded string") +=end + def CGI::unescape(string) + str = string.dup + str.gsub!(/\+/n, ' ') + str.gsub!(/%([0-9a-fA-F]{2})/n){ [$1.hex].pack("c") } + str + end + + +=begin +=== ESCAPE HTML &"<> + CGI::escapeHTML("string") +=end + def CGI::escapeHTML(string) + str = string.dup + str.gsub!(/&/n, '&') + str.gsub!(/\"/n, '"') + str.gsub!(/>/n, '>') + str.gsub!(/' + when /\Alt\z/ni then '<' + when /\A#(\d+)\z/n then Integer($1).chr + when /\A#x([0-9a-f]+)\z/ni then $1.hex.chr + end + } + str + end + + +=begin +=== ESCAPE ELEMENT + print CGI::escapeElement("
", "A", "IMG") + # "
<A HREF="url"></A>" + + print CGI::escapeElement("
", ["A", "IMG"]) + # "
<A HREF="url"></A>" +=end + def CGI::escapeElement(string, *element) + str = string.dup + str.gsub!(/<\/?(?:#{element.join("|")})(?!\w)(?:.|\n)*?>/ni){ + CGI::escapeHTML($&) + } + str + end + + +=begin +=== UNESCAPE ELEMENT + print CGI::unescapeElement( + CGI::escapeHTML("
"), "A", "IMG") + # "<BR>" + + print CGI::unescapeElement( + CGI::escapeHTML("
"), ["A", "IMG"]) + # "<BR>" +=end + def CGI::unescapeElement(string, *element) + str = string.dup + str.gsub!(/<\/?(?:#{element.join("|")})(?!\w)(?:.|\n)*?>/ni){ + CGI::unescapeHTML($&) + } + str + end + + +=begin +=== MAKE RFC1123 DATE STRING + CGI::rfc1123_date(Time.now) + # Sut, 1 Jan 2000 00:00:00 GMT +=end + def CGI::rfc1123_date(time) + t = time.clone.gmtime + return format("%s, %.2d %s %d %.2d:%.2d:%.2d GMT", + RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year, + t.hour, t.min, t.sec) + end + + +=begin +=== MAKE HTTP HEADER STRING + header + # Content-Type: text/html + + header("text/plain") + # Content-Type: text/plain + + header({"nph" => true, + "status" => "OK", # == "200 OK" + # "status" => "200 GOOD", + "server" => ENV['SERVER_SOFTWARE'], + "connection" => "close", + "type" => "text/html", + "charset" => "iso-2022-jp", + # Content-Type: text/html; charset=iso-2022-jp + "language" => "ja", + "expires" => Time.now + 30, + "cookie" => [cookie1, cookie2], + "my_header1" => "my_value" + "my_header2" => "my_value"}) + +header will not convert charset. + +status: + "OK" --> "200 OK" + "PARTIAL_CONTENT" --> "206 Partial Content" + "MULTIPLE_CHOICES" --> "300 Multiple Choices" + "MOVED" --> "301 Moved Permanently" + "REDIRECT" --> "302 Found" + "NOT_MODIFIED" --> "304 Not Modified" + "BAD_REQUEST" --> "400 Bad Request" + "AUTH_REQUIRED" --> "401 Authorization Required" + "FORBIDDEN" --> "403 Forbidden" + "NOT_FOUND" --> "404 Not Found" + "METHOD_NOT_ALLOWED" --> "405 Method Not Allowed" + "NOT_ACCEPTABLE" --> "406 Not Acceptable" + "LENGTH_REQUIRED" --> "411 Length Required" + "PRECONDITION_FAILED" --> "412 Rrecondition Failed" + "SERVER_ERROR" --> "500 Internal Server Error" + "NOT_IMPLEMENTED" --> "501 Method Not Implemented" + "BAD_GATEWAY" --> "502 Bad Gateway" + "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates" + +=end + def header(options = "text/html") + + buf = "" + + if options.kind_of?(String) + options = { "type" => options } + end + + unless options.key?("type") + options["type"] = "text/html" + end + + if options.key?("charset") + options["type"].concat( "; charset=" ) + options["type"].concat( options.delete("charset") ) + end + + if options.delete("nph") or (/IIS/n === env_table['SERVER_SOFTWARE']) + buf.concat( (env_table["SERVER_PROTOCOL"] or "HTTP/1.0") + " " ) + buf.concat( (HTTP_STATUS[options["status"]] or + options["status"] or + "200 OK" + ) + EOL + ) + buf.concat( + "Date: " + CGI::rfc1123_date(Time.now) + EOL + ) + + unless options.key?("server") + options["server"] = (env_table['SERVER_SOFTWARE'] or "") + end + + unless options.key?("connection") + options["connection"] = "close" + end + + end + options.delete("status") + + if options.key?("server") + buf.concat("Server: " + options.delete("server") + EOL) + end + + if options.key?("connection") + buf.concat("Connection: " + options.delete("connection") + EOL) + end + + buf.concat("Content-Type: " + options.delete("type") + EOL) + + if options.key?("length") + buf.concat("Content-Length: " + options.delete("length").to_s + EOL) + end + + if options.key?("language") + buf.concat("Content-Language: " + options.delete("language") + EOL) + end + + if options.key?("expires") + buf.concat("Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL) + end + + if options.key?("cookie") + if options["cookie"].kind_of?(String) or + options["cookie"].kind_of?(Cookie) + buf.concat("Set-Cookie: " + options.delete("cookie").to_s + EOL) + elsif options["cookie"].kind_of?(Array) + options.delete("cookie").each{|cookie| + buf.concat("Set-Cookie: " + cookie.to_s + EOL) + } + elsif options["cookie"].kind_of?(Hash) + options.delete("cookie").each_value{|cookie| + buf.concat("Set-Cookie: " + cookie.to_s + EOL) + } + end + end + if @output_cookies + for cookie in @output_cookies + buf.concat("Set-Cookie: " + cookie.to_s + EOL) + end + end + + options.each{|key, value| + buf.concat(key + ": " + value + EOL) + } + + if env_table['MOD_RUBY'] + buf.scan(/([^:]+): (.+)#{EOL}/n){ + Apache::request[$1] = $2 + } + Apache::request.send_http_header + '' + else + buf + EOL + end + + end # header() + + +=begin +=== PRINT HTTP HEADER AND STRING TO $> + cgi = CGI.new + cgi.out{ "string" } + # Content-Type: text/html + # Content-Length: 6 + # + # string + + cgi.out("text/plain"){ "string" } + # Content-Type: text/plain + # Content-Length: 6 + # + # string + + cgi.out({"nph" => true, + "status" => "OK", # == "200 OK" + "server" => ENV['SERVER_SOFTWARE'], + "connection" => "close", + "type" => "text/html", + "charset" => "iso-2022-jp", + # Content-Type: text/html; charset=iso-2022-jp + "language" => "ja", + "expires" => Time.now + (3600 * 24 * 30), + "cookie" => [cookie1, cookie2], + "my_header1" => "my_value", + "my_header2" => "my_value"}){ "string" } + +if charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then +convert string charset, and set language to "ja". + +=end + def out(options = "text/html") + + options = { "type" => options } if options.kind_of?(String) + content = yield + + if options.key?("charset") + require "nkf" + case options["charset"] + when /iso-2022-jp/ni + content = NKF::nkf('-j', content) + options["language"] = "ja" unless options.key?("language") + when /euc-jp/ni + content = NKF::nkf('-e', content) + options["language"] = "ja" unless options.key?("language") + when /shift_jis/ni + content = NKF::nkf('-s', content) + options["language"] = "ja" unless options.key?("language") + end + end + + options["length"] = content.length.to_s + output = stdoutput + output.binmode if defined? output.binmode + output.print header(options) + output.print content + end + + +=begin +=== PRINT + cgi = CGI.new + cgi.print # default: cgi.print == $>.print +=end + def print(*options) + stdoutput.print(*options) + end + + +=begin +=== MAKE COOKIE OBJECT + cookie1 = CGI::Cookie::new("name", "value1", "value2", ...) + cookie1 = CGI::Cookie::new({"name" => "name", "value" => "value"}) + cookie1 = CGI::Cookie::new({'name' => 'name', + 'value' => ['value1', 'value2', ...], + 'path' => 'path', # optional + 'domain' => 'domain', # optional + 'expires' => Time.now, # optional + 'secure' => true # optional + }) + + cgi.out({"cookie" => [cookie1, cookie2]}){ "string" } + + name = cookie1.name + values = cookie1.value + path = cookie1.path + domain = cookie1.domain + expires = cookie1.expires + secure = cookie1.secure + + cookie1.name = 'name' + cookie1.value = ['value1', 'value2', ...] + cookie1.path = 'path' + cookie1.domain = 'domain' + cookie1.expires = Time.now + 30 + cookie1.secure = true +=end + require "delegate" + class Cookie < SimpleDelegator + + def initialize(name = "", *value) + options = if name.kind_of?(String) + { "name" => name, "value" => value } + else + name + end + unless options.key?("name") + raise ArgumentError, "`name' required" + end + + @name = options["name"] + @value = Array(options["value"]) + @path = options["path"] + @domain = options["domain"] + @expires = options["expires"] + @secure = options["secure"] == true ? true : false + + super(@value) + end + + attr_accessor("name", "value", "path", "domain", "expires") + attr_reader("secure") + def secure=(val) + @secure = val if val == true or val == false + @secure + end + + def to_s + buf = "" + buf.concat(@name + '=') + + if @value.kind_of?(String) + buf.concat CGI::escape(@value) + else + buf.concat(@value.filter{|v| CGI::escape(v) }.join("&")) + end + + if @domain + buf.concat('; domain=' + @domain) + end + + if @path + buf.concat('; path=' + @path) + end + + if @expires + buf.concat('; expires=' + CGI::rfc1123_date(@expires)) + end + + if @secure == true + buf.concat('; secure') + end + + buf + end + + end # class Cookie + + +=begin +=== PARSE RAW COOKIE STRING + cookies = CGI::Cookie::parse("raw_cookie_string") + # { "name1" => cookie1, "name2" => cookie2, ... } +=end + def Cookie::parse(raw_cookie) + cookies = Hash.new([]) + + raw_cookie.split('; ').each do |pairs| + name, values = pairs.split('=',2) + name = CGI::unescape(name) + values = values.split('&').filter{|v| CGI::unescape(v) } + if cookies.key?(name) + cookies[name].value.push(*values) + else + cookies[name] = Cookie::new({ "name" => name, "value" => values }) + end + end + + cookies + end + + +=begin +=== PARSE QUERY STRING + params = CGI::parse("query_string") + # {"name1" => ["value1", "value2", ...], + # "name2" => ["value1", "value2", ...], ... } +=end + def CGI::parse(query) + params = Hash.new([]) + + query.split(/[&;]/n).each do |pairs| + key, value = pairs.split('=',2).filter{|v| CGI::unescape(v) } + if params.key?(key) + params[key].push(value) + else + params[key] = [value] + end + end + + params + end + + + module QueryExtension + + %w[ CONTENT_LENGTH SERVER_PORT ]. + each{|env| + eval( <<-END ) + def #{env.sub(/^HTTP_/n, '').downcase} + env_table["#{env}"] && Integer(env_table["#{env}"]) + end + END + } + + %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO + PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST + REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME + SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE + + HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING + HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST + HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ]. + each{|env| + eval( <<-END ) + def #{env.sub(/^HTTP_/n, '').downcase} + env_table["#{env}"] + end + END + } + + def raw_cookie + env_table["HTTP_COOKIE"] + end + + def raw_cookie2 + env_table["HTTP_COOKIE2"] + end + + attr_accessor("cookies") + attr("params") + def params=(hash) + @params.clear + @params.update(hash) + end + + def read_multipart(boundary, content_length) + params = Hash.new([]) + boundary = "--" + boundary + buf = "" + bufsize = 10 * 1024 + + # start multipart/form-data + stdinput.binmode + content_length -= stdinput.read((boundary + EOL).size).size + + require "tempfile.rb" + + until -1 == content_length + head = nil + body = Tempfile.new("CGI") + body.binmode + + until head and (/#{boundary}(?:#{EOL}|--)/n === buf) + + if (not head) and (/#{EOL}#{EOL}/n === buf) + buf.sub!(/\A((?:.|\n)*?#{EOL})#{EOL}/n){ + head = $1.dup + "" + } + next + end + + if head and ( (EOL + boundary + EOL).size < buf.size ) + body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)] + buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" + end + + c = if bufsize < content_length + stdinput.read(bufsize) or '' + else + stdinput.read(content_length) or '' + end + buf.concat c + content_length -= c.size + + end + + buf.sub!(/\A((?:.|\n)*?)(?:#{EOL})?#{boundary}(#{EOL}|--)/n){ + body.print $1 + if "--" == $2 + content_length = -1 + end + "" + } + + body.rewind + + eval <<-END + def body.local_path + #{body.path.dump} + end + END + + /Content-Disposition:.* filename="?([^\";]*)"?/ni === head + eval <<-END + def body.original_filename + #{ + filename = ($1 or "").dup + if (/Mac/ni === env_table['HTTP_USER_AGENT']) and + (/Mozilla/ni === env_table['HTTP_USER_AGENT']) and + (not /MSIE/ni === env_table['HTTP_USER_AGENT']) + CGI::unescape(filename) + else + filename + end.dump + } + end + END + + /Content-Type: (.*)/ni === head + eval <<-END + def body.content_type + #{($1 or "").dump} + end + END + + /Content-Disposition:.* name="?([^\";]*)"?/ni === head + name = $1.dup + + if params.key?(name) + params[name].push(body) + else + params[name] = [body] + end + + end + + params + end # read_multipart + private :read_multipart + + # offline mode. read name=value pairs on standard input. + def read_from_cmdline + require "shellwords.rb" + + unless ARGV.empty? + str = ARGV.join(' ') + else + if STDIN.tty? + STDERR.print( + %|(offline mode: enter name=value pairs on standard input)\n| + ) + end + str = readlines.join(' ') + str.gsub!(/\n/n, '') + end + + str.gsub!(/\\=/n, '%3D') + str.gsub!(/\\&/n, '%26') + + words = Shellwords.shellwords(str) + + if words.find{|x| /=/n === x } + words.join('&') + else + words.join('+') + end + end + private :read_from_cmdline + + def initialize_query() + if ("POST" == env_table['REQUEST_METHOD']) and + (%r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n === + env_table['CONTENT_TYPE']) + boundary = $1.dup + @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) + else + @params = CGI::parse( + case env_table['REQUEST_METHOD'] + when "GET", "HEAD" + if env_table['MOD_RUBY'] + Apache::request.args or "" + else + env_table['QUERY_STRING'] or "" + end + when "POST" + stdinput.binmode + stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or '' + else + read_from_cmdline + end + ) + end + + @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or + env_table['COOKIE'] or "")) + + end + private :initialize_query + + def [](*args) + @params[*args] + end + + def keys(*args) + @params.keys(*args) + end + + def has_key?(*args) + @params.has_key?(*args) + end + alias key? has_key? + alias include? has_key? + + end # QueryExtension + + +=begin +=== HTML PRETTY FORMAT + print CGI::pretty("") + # + # + # + # + + print CGI::pretty("", "\t") + # + # + # + # +=end + def CGI::pretty_shift(string, shift = " ") + shift = " " if true == shift + str = string.dup + str.gsub!(/\n(?!\z)/n, "\n" + shift) + str + end + def CGI::pretty_nest(string, shift = " ") + str = string.dup + str.gsub!(/(<(\w+).*?>)((?:.|\n)*?)(<\/\2>)/n){ + $1 + CGI::pretty_shift(CGI::pretty_nest($3, shift), shift) + $4 + } + str + end + def CGI::pretty(string, shift = " ") + str = string.dup + str.gsub!(/<(?:.|\n)*?>/n, "\n\\0") + str.gsub!(/<(?:.|\n)*?>(?!\n)/n, "\\0\n") + CGI::pretty_nest(str, shift) + end + + +=begin +== HTML ELEMENTS + + cgi = CGI.new("html3") # add HTML generation methods + cgi.element + cgi.element{ "string" } + cgi.element({ "ATTRILUTE1" => "value1", "ATTRIBUTE2" => "value2" }) + cgi.element({ "ATTRILUTE1" => "value1", "ATTRIBUTE2" => "value2" }){ "string" } + + # add HTML generation methods + CGI.new("html3") # html3.2 + CGI.new("html4") # html4.0 (Strict) + CGI.new("html4Tr") # html4.0 Transitional + CGI.new("html4Fr") # html4.0 Frameset + +=end + + + module TagMaker + + # - - + def nn_element_def(element) + <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase) + "" + + if iterator? + yield.to_s + else + "" + end + + "" + END + end + + # - O EMPTY + def nOE_element_def(element) + <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase) + "" + END + end + + # O O or - O + def nO_element_def(element) + <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase) + "" + + if iterator? + yield.to_s + "" + else + "" + end + END + end + + end # TagMaker + + + module HtmlExtension + extend TagMaker + + +=begin +=== A ELEMENT + a("url") + # = a({ "HREF" => "url" }) +=end + def a(href = "") + attributes = if href.kind_of?(String) + { "HREF" => href } + else + href + end + if iterator? + super(attributes){ yield } + else + super(attributes) + end + end + + +=begin +=== BASE ELEMENT + base("url") + # = base({ "HREF" => "url" }) +=end + def base(href = "") + attributes = if href.kind_of?(String) + { "HREF" => href } + else + href + end + if iterator? + super(attributes){ yield } + else + super(attributes) + end + end + + +=begin +=== BLOCKQUOTE ELEMENT + blockquote("url"){ "string" } + # = blockquote({ "CITE" => "url" }){ "string" } +=end + def blockquote(cite = nil) + attributes = if cite.kind_of?(String) + { "CITE" => cite } + else + cite or "" + end + if iterator? + super(attributes){ yield } + else + super(attributes) + end + end + + +=begin +=== CAPTION ELEMENT + caption("align"){ "string" } + # = caption({ "ALIGN" => "align" }){ "string" } +=end + def caption(align = nil) + attributes = if align.kind_of?(String) + { "ALIGN" => align } + else + align or "" + end + if iterator? + super(attributes){ yield } + else + super(attributes) + end + end + + +=begin +=== CHECKBOX + checkbox("name") + # = checkbox({ "NAME" => "name" }) + + checkbox("name", "value") + # = checkbox({ "NAME" => "name", "VALUE" => "value" }) + + checkbox("name", "value", true) + # = checkbox({ "NAME" => "name", "VALUE" => "value", "CHECKED" => true }) +=end + def checkbox(name = "", value = nil, checked = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "checkbox", "NAME" => name, + "VALUE" => value, "CHECKED" => checked } + else + name["TYPE"] = "checkbox" + name + end + input(attributes) + end + + +=begin +=== CHECKBOX_GROUP + checkbox_group("name", "foo", "bar", "baz") + # foo + # bar + # baz + + checkbox_group("name", ["foo"], ["bar", true], "baz") + # foo + # bar + # baz + + checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # Foo + # Bar + # Baz + + checkbox_group({ "NAME" => "name", + "VALUES" => ["foo", "bar", "baz"] }) + + checkbox_group({ "NAME" => "name", + "VALUES" => [["foo"], ["bar", true], "baz"] }) + + checkbox_group({ "NAME" => "name", + "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] }) +=end + def checkbox_group(name = "", *values) + if name.kind_of?(Hash) + values = name["VALUES"] + name = name["NAME"] + end + values.collect{|value| + if value.kind_of?(String) + checkbox(name, value) + value + else + if value[value.size - 1] == true + checkbox(name, value[0], true) + + value[value.size - 2] + else + checkbox(name, value[0]) + + value[value.size - 1] + end + end + }.to_s + end + + +=begin +=== FILE_FIELD + file_field("name") + # + + file_field("name", 40) + # + + file_field("name", 40, 100) + # + + file_field({ "NAME" => "name", "SIZE" => 40 }) + # +=end + def file_field(name = "", size = 20, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "file", "NAME" => name, + "SIZE" => size.to_s } + else + name["TYPE"] = "file" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + +=begin +=== FORM ELEMENT + form{ "string" } + #
string
+ + form("get"){ "string" } + #
string
+ + form("get", "url"){ "string" } + #
string
+ + form({"METHOD" => "post", ENCTYPE => "enctype"}){ "string" } + #
string
+=end + def form(method = "post", action = nil, enctype = "application/x-www-form-urlencoded") + attributes = if method.kind_of?(String) + { "METHOD" => method, "ACTION" => action, + "ENCTYPE" => enctype } + else + unless method.key?("METHOD") + method["METHOD"] = method + end + unless method.key?("ENCTYPE") + method["ENCTYPE"] = enctype + end + method + end + if iterator? + body = yield + else + body = "" + end + if @output_hidden + hidden = @output_hidden.collect{|k,v| + "" + }.to_s + body.concat hidden + end + super(attributes){body} + end + +=begin +=== HIDDEN FIELD + hidden("name") + # + + hidden("name", "value") + # + + hidden({ "NAME" => "name", "VALUE" => "reset", "ID" => "foo" }) + # +=end + def hidden(name = "", value = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "hidden", "NAME" => name, "VALUE" => value } + else + name["TYPE"] = "hidden" + name + end + input(attributes) + end + + +=begin +=== HTML ELEMENT + + html{ "string" } + # string + + html({ "LANG" => "ja" }){ "string" } + # string + + html({ "DOCTYPE" => false }){ "string" } + # string + + html({ "DOCTYPE" => '' }){ "string" } + # string + + html({ "PRETTY" => " " }){ "" } + # + # + # + # + # + + html({ "PRETTY" => "\t" }){ "" } + # + # + # + # + # + + html("PRETTY"){ "" } + # = html({ "PRETTY" => " " }){ "" } + + html(if $VERBOSE then "PRETTY" end){ "HTML string" } + +=end + def html(attributes = {}) + if nil == attributes + attributes = {} + elsif "PRETTY" == attributes + attributes = { "PRETTY" => true } + end + pretty = attributes.delete("PRETTY") + buf = "" + + if attributes.key?("DOCTYPE") + if attributes["DOCTYPE"] + buf.concat( attributes.delete("DOCTYPE") ) + else + attributes.delete("DOCTYPE") + end + else + buf.concat( doctype ) + end + + if iterator? + buf.concat( super(attributes){ yield } ) + else + buf.concat( super(attributes) ) + end + + if pretty + CGI::pretty(buf, pretty) + else + buf + end + + end + + +=begin +=== IMAGE_BUTTON + image_button("url") + # + + image_button("url", "name", "string") + # + + image_button({ "SRC" => "url", "ATL" => "strng" }) + # +=end + def image_button(src = "", name = nil, alt = nil) + attributes = if src.kind_of?(String) + { "TYPE" => "image", "SRC" => src, "NAME" => name, + "ALT" => alt } + else + name["TYPE"] = "image" + name + end + input(attributes) + end + + +=begin +=== IMG ELEMENT + img("src", "alt", 100, 50) + # alt + + img({ "SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50 }) + # alt +=end + def img(src = "", alt = "", width = nil, height = nil) + attributes = if src.kind_of?(String) + { "SRC" => src, "ALT" => alt } + else + src + end + attributes["WIDTH"] = width.to_s if width + attributes["HEIGHT"] = height.to_s if height + super(attributes) + end + + +=begin +=== MULTIPART FORM + multipart_form{ "string" } + #
string
+ + multipart_form("url"){ "string" } + #
string
+=end + def multipart_form(action = nil, enctype = "multipart/form-data") + attributes = if action == nil + { "METHOD" => "post", "ENCTYPE" => enctype } + elsif action.kind_of?(String) + { "METHOD" => "post", "ACTION" => action, + "ENCTYPE" => enctype } + else + unless action.key?("METHOD") + action["METHOD"] = "post" + end + unless action.key?("ENCTYPE") + action["ENCTYPE"] = enctype + end + action + end + if iterator? + form(attributes){ yield } + else + form(attributes) + end + end + + +=begin +=== PASSWORD_FIELD + password_field("name") + # + + password_field("name", "value") + # + + password_field("password", "value", 80, 200) + # + + password_field({ "NAME" => "name", "VALUE" => "value" }) + # +=end + def password_field(name = "", value = nil, size = 40, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "password", "NAME" => name, + "VALUE" => value, "SIZE" => size.to_s } + else + name["TYPE"] = "password" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + +=begin +=== POPUP_MENU + popup_menu("name", "foo", "bar", "baz") + # + + popup_menu("name", ["foo"], ["bar", true], "baz") + # + + popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # + + popup_menu({"NAME" => "name", "SIZE" => 2, "MULTIPLE" => true, + "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] }) + # +=end + def popup_menu(name = "", *values) + + if name.kind_of?(Hash) + values = name["VALUES"] + size = name["SIZE"].to_s if name["SIZE"] + multiple = name["MULTIPLE"] + name = name["NAME"] + else + size = nil + multiple = nil + end + + select({ "NAME" => name, "SIZE" => size, + "MULTIPLE" => multiple }){ + values.collect{|value| + if value.kind_of?(String) + option({ "VALUE" => value }){ value } + else + if value[value.size - 1] == true + option({ "VALUE" => value[0], "SELECTED" => true }){ + value[value.size - 2] + } + else + option({ "VALUE" => value[0] }){ + value[value.size - 1] + } + end + end + }.to_s + } + + end + + +=begin +=== RADIO_BUTTON + radio_button("name", "value") + # + + radio_button("name", "value", true) + # + + radio_button({ "NAME" => "name", "VALUE" => "value", "ID" => "foo" }) + # +=end + def radio_button(name = "", value = nil, checked = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "radio", "NAME" => name, + "VALUE" => value, "CHECKED" => checked } + else + name["TYPE"] = "radio" + name + end + input(attributes) + end + + +=begin +=== RADIO_GROUP + radio_group("name", "foo", "bar", "baz") + # foo + # bar + # baz + + radio_group("name", ["foo"], ["bar", true], "baz") + # foo + # bar + # baz + + radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # Foo + # Bar + # Baz + + radio_group({ "NAME" => "name", + "VALUES" => ["foo", "bar", "baz"] }) + + radio_group({ "NAME" => "name", + "VALUES" => [["foo"], ["bar", true], "baz"] }) + + radio_group({ "NAME" => "name", + "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] }) +=end + def radio_group(name = "", *values) + if name.kind_of?(Hash) + values = name["VALUES"] + name = name["NAME"] + end + values.collect{|value| + if value.kind_of?(String) + radio_button(name, value) + value + else + if value[value.size - 1] == true + radio_button(name, value[0], true) + + value[value.size - 2] + else + radio_button(name, value[0]) + + value[value.size - 1] + end + end + }.to_s + end + + +=begin +=== RESET BUTTON + reset + # + + reset("reset") + # + + reset({ "VALUE" => "reset", "ID" => "foo" }) + # +=end + def reset(value = nil, name = nil) + attributes = if (not value) or value.kind_of?(String) + { "TYPE" => "reset", "VALUE" => value, "NAME" => name } + else + value["TYPE"] = "reset" + value + end + input(attributes) + end + + +=begin +=== SCROLLING_LIST + scrolling_list({"NAME" => "name", "SIZE" => 2, "MULTIPLE" => true, + "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] }) + # +=end + alias scrolling_list popup_menu + + +=begin +=== SUBMIT BUTTON + submit + # + + submit("ok") + # + + submit("ok", "button1") + # + + submit({ "VALUE" => "ok", "NAME" => "button1", "ID" => "foo" }) + # +=end + def submit(value = nil, name = nil) + attributes = if (not value) or value.kind_of?(String) + { "TYPE" => "submit", "VALUE" => value, "NAME" => name } + else + value["TYPE"] = "submit" + value + end + input(attributes) + end + + +=begin +=== TEXT_FIELD + text_field("name") + # + + text_field("name", "value") + # + + text_field("name", "value", 80) + # + + text_field("name", "value", 80, 200) + # + + text_field({ "NAME" => "name", "VALUE" => "value" }) + # +=end + def text_field(name = "", value = nil, size = 40, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "text", "NAME" => name, "VALUE" => value, + "SIZE" => size.to_s } + else + name["TYPE"] = "text" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + +=begin +=== TEXTAREA ELEMENT + + textarea("name") + # = textarea({ "NAME" => "name", "COLS" => 70, "ROWS" => 10 }) + + textarea("name", 40, 5) + # = textarea({ "NAME" => "name", "COLS" => 40, "ROWS" => 5 }) +=end + def textarea(name = "", cols = 70, rows = 10) + attributes = if name.kind_of?(String) + { "NAME" => name, "COLS" => cols.to_s, + "ROWS" => rows.to_s } + else + name + end + if iterator? + super(attributes){ yield } + else + super(attributes) + end + end + + end # HtmlExtension + + + module Html3 + extend TagMaker + + def doctype + %|| + end + + # - - + %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG DFN CODE SAMP KBD VAR + CITE FONT ADDRESS DIV center MAP APPLET PRE XMP LISTING DL OL UL DIR + MENU SELECT table TITLE STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM + BLOCKQUOTE CAPTION ]. + each{|element| + eval( <<-BEGIN + nn_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + # - O EMPTY + %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT ISINDEX META ]. + each{|element| + eval( <<-BEGIN + nOE_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + # O O or - O + %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr th td ]. + each{|element| + eval( <<-BEGIN + nO_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + end # Html3 + + + module Html4 + extend TagMaker + + def doctype + %|| + end + + # - - + %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD VAR CITE ABBR ACRONYM + SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT H1 H2 H3 H4 H5 H6 PRE Q + INS DEL DL OL UL LABEL SELECT OPTGROUP FIELDSET LEGEND BUTTON TABLE + TITLE STYLE SCRIPT NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]. + each{|element| + eval( <<-BEGIN + nn_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + # - O EMPTY + %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]. + each{|element| + eval( <<-BEGIN + nOE_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + # O O or - O + %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY COLGROUP TR TH TD HEAD]. + each{|element| + eval( <<-BEGIN + nO_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + end # Html4 + + + module Html4Tr + extend TagMaker + + def doctype + %|| + end + + # - - + %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN CODE SAMP KBD VAR CITE + ABBR ACRONYM FONT SUB SUP SPAN BDO ADDRESS DIV CENTER MAP OBJECT + APPLET H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL DIR MENU LABEL SELECT + OPTGROUP FIELDSET LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE + SCRIPT NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]. + each{|element| + eval( <<-BEGIN + nn_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + # - O EMPTY + %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT COL ISINDEX META ]. + each{|element| + eval( <<-BEGIN + nOE_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + # O O or - O + %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY COLGROUP TR TH TD HEAD ]. + each{|element| + eval( <<-BEGIN + nO_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + end # Html4Tr + + + module Html4Fr + include Html4Tr + extend TagMaker + + def doctype + %|| + end + + # - - + %w[ FRAMESET ]. + each{|element| + eval( <<-BEGIN + nn_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + # - O EMPTY + %w[ FRAME ]. + each{|element| + eval( <<-BEGIN + nOE_element_def(element) + <<-END ) + def #{element.downcase}(attributes = {}) + BEGIN + end + END + } + + end # Html4Fr + + + def initialize(type = "query") + extend QueryExtension + initialize_query() + # @params, @cookies initialized in initialize_query + + case type + when "html3" + extend Html3 + extend HtmlExtension + when "html4" + extend Html4 + extend HtmlExtension + when "html4Tr" + extend Html4Tr + extend HtmlExtension + when "html4Fr" + extend Html4Fr + extend HtmlExtension + end + end +end + + +=begin + +== HISTRY + +=== Version 1.00 - wakou + +1999/09/13 23:00:58 + +- COUTION! name change. CGI.rb --> cgi.rb + +- CGI#auth_type, CGI#content_length, CGI#content_type, ... +if not ENV included it, then return nil. + +- CGI#content_length and CGI#server_port return Integer. + +- if not CGI#params.include?('name'), then CGI#params['name'] return []. + +- if not CGI#cookies.include?('name'), then CGI#cookies['name'] return []. + +=== Version 0.41 - wakou + +1999/08/05 18:04:59 + +- typo. thanks to MJ Ray + HTTP_STATUS["NOT_INPLEMENTED"] --> HTTP_STATUS["NOT_IMPLEMENTED"] + +=== Version 0.40 - wakou + +1999/07/20 20:44:31 + +- COUTION! incompatible change. + sorry, but probably this change is last big incompatible change. + +- CGI::print --> CGI#out + + cgi = CGI.new + cgi.out{"string"} # old: CGI::print{"string"} + +- CGI::cookie --> CGI::Cookie::new + + cookie1 = CGI::Cookie::new # old: CGI::cookie + +- CGI::header --> CGI#header + +=== Version 0.30 - wakou + +1999/06/29 06:50:21 + +- COUTION! incompatible change. + query = CGI.new + cookies = query.cookies # old: query.cookie + values = query.cookies[name] # old: query.cookie[name] + +=== Version 0.24 - wakou + +1999/06/21 21:05:57 + +- CGI::Cookie::parse() return { name => CGI::Cookie object } pairs. + +=== Version 0.23 - wakou + +1999/06/20 23:29:12 + +- modified a bit to clear module separation. + +=== Version 0.22 - matz + +Mon Jun 14 17:49:32 JST 1999 + +- Cookies are now CGI::Cookie objects. +- Cookie modeled after CGI::Cookie.pm. + +=== Version 0.21 - matz + +Fri Jun 11 11:19:11 JST 1999 + +- modified a bit to clear module separation. + +=== Version 0.20 - wakou + +1999/06/03 06:48:15 + +- support for multipart form. + +=== Version 0.10 - wakou + +1999/05/24 07:05:41 + +- first release. + +=end diff --git a/lib/jcode.rb b/lib/jcode.rb index 92b9284471..7a4cddd693 100644 --- a/lib/jcode.rb +++ b/lib/jcode.rb @@ -4,58 +4,82 @@ $vsave, $VERBOSE = $VERBOSE, FALSE class String printf STDERR, "feel free for some warnings:\n" if $VERBOSE - def jlength - self.split(//).length - end - - alias original_succ succ - private :original_succ + PATTERN_SJIS = '[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc]' + PATTERN_EUC = '[\xa1-\xfe][\xa1-\xfe]' + PATTERN_UTF8 = '[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf]' + + RE_SJIS = Regexp.new(PATTERN_SJIS, 'n') + RE_EUC = Regexp.new(PATTERN_EUC, 'n') + RE_UTF8 = Regexp.new(PATTERN_UTF8, 'n') + + SUCC = {} + SUCC['s'] = Hash.new(1) + for i in 0 .. 0x3f + SUCC['s'][i.chr] = 0x40 - i + end + SUCC['s']["\x7e"] = 0x80 - 0x7e + SUCC['s']["\xfd"] = 0x100 - 0xfd + SUCC['s']["\xfe"] = 0x100 - 0xfe + SUCC['s']["\xff"] = 0x100 - 0xff + SUCC['e'] = Hash.new(1) + for i in 0 .. 0xa0 + SUCC['e'][i.chr] = 0xa1 - i + end + SUCC['e']["\xfe"] = 2 + SUCC['u'] = Hash.new(1) + for i in 0 .. 0x7f + SUCC['u'][i.chr] = 0x80 - i + end + SUCC['u']["\xbf"] = 0x100 - 0xbf def mbchar? case $KCODE[0] when ?s, ?S - self =~ /[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc]/n + self =~ RE_SJIS when ?e, ?E - self =~ /[\xa1-\xfe][\xa1-\xfe]/n + self =~ RE_EUC + when ?u, ?U + self =~ RE_UTF8 else - false + nil end end - def succ - if self[-2] and self[-2, 2].mbchar? - s = self.dup - s[-1] += 1 - s[-1] += 1 unless s[-2, 2].mbchar? - return s + def end_regexp + case $KCODE[0] + when ?s, ?S + /#{PATTERN_SJIS}$/o + when ?e, ?E + /#{PATTERN_EUC}$/o + when ?u, ?U + /#{PATTERN_UTF8}$/o else - original_succ + /.$/o end end - def upto(to) - return if self > to + alias original_succ! succ! + private :original_succ! - curr = self - tail = self[-2..-1] - if tail.length == 2 and tail =~ /^.$/ then - if self[0..-2] == to[0..-2] - first = self[-2].chr - for c in self[-1] .. to[-1] - if (first+c.chr).mbchar? - yield self[0..-2]+c.chr - end - end - end + alias original_succ succ + private :original_succ + + def succ! + reg = end_regexp + if self =~ reg + succ_table = SUCC[$KCODE[0,1].downcase] + begin + self[-1] += succ_table[self[-1]] + self[-2] += 1 if self[-1] == 0 + end while self !~ reg + self else - loop do - yield curr - return if curr == to - curr = curr.succ - return if curr.length > to.length - end + original_succ! end - return nil + end + + def succ + (str = self.dup).succ! or str end private @@ -159,9 +183,24 @@ class String (str = self.dup).chop! or str end + def jlength + self.gsub(/[^\Wa-zA-Z_\d]/, ' ').length + end + alias jsize jlength + def jcount(str) self.delete("^#{str}").jlength end + def each_char + if iterator? + scan(/./) do |x| + yield x + end + else + scan(/./) + end + end + end $VERBOSE = $vsave -- cgit v1.2.3